Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
783cf17
chore(db): drop retired usage columns and compatibility scaffolding (…
icecrasher321 Sep 16, 2026
0bef03f
fix(browser): improve snapshots, native controls and action reliabili…
waleedlatif1 Sep 16, 2026
83c165a
feat(access-requests): request and review permission access (#7871)
waleedlatif1 Sep 16, 2026
dec53e2
fix(access-requests): scope previews and audit events correctly (#7873)
waleedlatif1 Sep 16, 2026
c568401
fix(sidebar): extend faded labels until row actions appear (#7874)
waleedlatif1 Sep 16, 2026
a94fce5
fix(workspace): hydrate access policy before rendering chat (#7875)
waleedlatif1 Sep 16, 2026
f760a60
fix(docs): correct db:push backfill description (#7877)
waleedlatif1 Sep 16, 2026
31ad74b
feat(library): AI Personal Assistant vs. AI Agent Builder: What's the…
icecrasher321 Sep 16, 2026
360d653
fix(credentials): restore scoped Slack bot reconnects (#7879)
TheodoreSpeaks Sep 16, 2026
de0f118
feat(sso): let organizations require single sign-on (#7881)
waleedlatif1 Sep 16, 2026
713f247
improvement(credentials): allow connecting accounts with different em…
TheodoreSpeaks Sep 16, 2026
f9ad878
fix(access-requests): align permission checks and pending request flo…
waleedlatif1 Sep 16, 2026
04aae63
fix(credentials): clean up personal secrets in archived workspaces (#…
icecrasher321 Sep 16, 2026
8d1f07b
improvement(integrations): consolidate generated metadata (#7887)
waleedlatif1 Sep 16, 2026
2ee0708
feat(insights): add organization activity dashboards (#7884)
waleedlatif1 Sep 16, 2026
f6bc723
fix(security): close fail-open paths found by the integration suite (…
waleedlatif1 Sep 16, 2026
000356f
feat(credential-groups): allowlist integrations by workspace (#7885)
TheodoreSpeaks Sep 16, 2026
95b5b76
fix(search): bound retrieval and avoid repeated turn admission (#7888)
icecrasher321 Sep 16, 2026
212a3a9
fix(access-control): keep the settings page open while an organizatio…
waleedlatif1 Sep 16, 2026
55ccc5d
feat(search-mcp): persist client-attributed tool activity (#7892)
waleedlatif1 Sep 16, 2026
42a413b
fix(slack-search): wait for content before starting replies (#7891)
TheodoreSpeaks Sep 16, 2026
bfa86a5
fix(browser): recover stalled screenshots and preserve image coordina…
waleedlatif1 Sep 16, 2026
cb67145
fix(search): surface Google service account authorization errors (#7893)
waleedlatif1 Sep 16, 2026
2243890
fix(workflows): resolve finished resume runs on the run status endpoi…
waleedlatif1 Sep 16, 2026
294de5f
chore(auth): diagnose unexpected managed OAuth callback failures (#7896)
icecrasher321 Sep 16, 2026
664ca82
fix(search): preserve Google sync diagnostics and retry transient fai…
waleedlatif1 Sep 16, 2026
90a05d9
feat(ci): promote Trigger.dev tasks in lockstep with the ECS traffic …
TheodoreSpeaks Sep 16, 2026
9cfa049
fix(search): complete sync failure diagnostic context (#7898)
waleedlatif1 Sep 16, 2026
6be9008
fix(search): retain permission sync failures and clarify progress (#7…
waleedlatif1 Sep 16, 2026
ff25be7
fix(search): preserve indexed vector retrieval and literal source tex…
icecrasher321 Sep 17, 2026
6ea53c0
fix(slack): allow installation before organization setup (#7900)
TheodoreSpeaks Sep 17, 2026
a730e59
fix(workspace-fork): scope and batch preview revisions (#7901)
icecrasher321 Sep 17, 2026
bf37391
fix(ci): prepare tasks before app rollout and promote at cutover (#7902)
TheodoreSpeaks Sep 17, 2026
1d08664
fix(knowledge): bound KB block vector retrieval (#7903)
icecrasher321 Sep 17, 2026
6dffde3
chore(ci): simplify Trigger release workflow (#7905)
TheodoreSpeaks Sep 17, 2026
25a2136
feat(coda): add Coda integration with API-token credential and resour…
waleedlatif1 Sep 17, 2026
32d98f4
fix(gmail): open search results in the indexed mailbox (#7910)
waleedlatif1 Sep 17, 2026
f7f678b
fix(knowledge): increase hosted rerank capacity (#7912)
TheodoreSpeaks Sep 17, 2026
f2a89aa
fix(insights): reduce activity aggregation overhead (#7908)
waleedlatif1 Sep 17, 2026
2a5b22c
fix(outbox): process cron batches outside HTTP requests (#7911)
icecrasher321 Sep 17, 2026
49fae45
fix(credential-groups): expose provider emails and align people rows …
TheodoreSpeaks Sep 17, 2026
afefd96
fix(file-search): bound dispatcher database work (#7909)
icecrasher321 Sep 17, 2026
59a364a
fix(credentials): render the credential selector with the editor comb…
waleedlatif1 Sep 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ If you prefer not to use Docker. **All commands run from the repository root unl
cd packages/db && bun run db:migrate && cd ../..
```

For ad-hoc schema iteration during development you can also use `bun run db:push` from `packages/db`, but `db:migrate` is the canonical command for both local and CI/CD setups.
For ad-hoc schema iteration during development you can also use `bun run db:push` from `packages/db`, but `db:migrate` is the canonical command for staging and production. `db:push` reconciles directly to the current schema without running versioned migration guards. For disposable local/dev databases, `bun run db:push --force` accepts Drizzle's data-loss prompts, including column drops.

4. **Run the Development Servers:**

Expand Down
28 changes: 28 additions & 0 deletions .github/scripts/get-ecr-image-digest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Read one ECR tag. Only ImageNotFound is optional; AWS and response errors fail.
set -euo pipefail
REPOSITORY="${1:?repository required}"
TAG="${2:?tag required}"
ALLOW_MISSING="${3:-}"
if [ -n "$ALLOW_MISSING" ] && [ "$ALLOW_MISSING" != '--allow-missing' ]; then
echo 'ERROR: expected --allow-missing or no third argument' >&2
exit 1
fi
export AWS_PAGER=''
aws ecr batch-get-image --repository-name "$REPOSITORY" --image-ids imageTag="$TAG" --output json |
ALLOW_MISSING="$ALLOW_MISSING" python3 -c '
import json, os, re, sys
response = json.load(sys.stdin)
images, failures = response["images"], response["failures"]
if failures:
if not images and len(failures) == 1 and failures[0]["failureCode"] == "ImageNotFound" and os.environ["ALLOW_MISSING"]:
print("")
sys.exit(0)
raise SystemExit("ERROR: ECR image lookup failed: " + ", ".join(f["failureCode"] for f in failures))
if len(images) != 1:
raise SystemExit("ERROR: expected exactly one ECR image")
digest = images[0]["imageId"]["imageDigest"]
if not re.fullmatch(r"sha256:[0-9a-f]{64}", digest):
raise SystemExit("ERROR: invalid ECR image digest")
print(digest)
'
20 changes: 20 additions & 0 deletions .github/scripts/promote-app-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Capture the cutover lower bound at the app tag move, after the image is built.
set -euo pipefail
REGISTRY="${1:?registry required}"
REPOSITORY="${2:?repository required}"
SOURCE_TAG="${3:?source tag required}"
DEPLOY_TAG="${4:?deploy tag required}"
: "${GITHUB_OUTPUT:?GitHub output file required}"
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
PREVIOUS=$(bash "$SCRIPT_DIR/get-ecr-image-digest.sh" "$REPOSITORY" "$DEPLOY_TAG" --allow-missing)
EPOCH=$(date +%s)
docker buildx imagetools create -t "$REGISTRY/$REPOSITORY:$DEPLOY_TAG" "$REGISTRY/$REPOSITORY:$SOURCE_TAG"
DIGEST=$(bash "$SCRIPT_DIR/get-ecr-image-digest.sh" "$REPOSITORY" "$DEPLOY_TAG")
CHANGED=true
if [ "$DIGEST" = "$PREVIOUS" ]; then CHANGED=false; fi
{
echo "retag_epoch=$EPOCH"
echo "app_image_digest=$DIGEST"
echo "app_image_changed=$CHANGED"
} >> "$GITHUB_OUTPUT"
162 changes: 162 additions & 0 deletions .github/scripts/wait-for-ecs-cutover.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#!/usr/bin/env bash
# Resolve a pushed app digest to CodePipeline -> CodeDeploy -> every ECS target's
# AllowTraffic event. An unchanged tag uses since-epoch=0 to verify the latest
# pipeline execution instead of assuming the tagged image is already serving.
# Usage: wait-for-ecs-cutover.sh <pipeline-name> <image-digest> <since-epoch>
set -euo pipefail

PIPELINE="${1:?pipeline name required}"
DIGEST="${2:?image digest required}"
SINCE_EPOCH="${3:?since-epoch required}"
POLL_INTERVAL="${POLL_INTERVAL:-15}"
OVERALL_TIMEOUT="${OVERALL_TIMEOUT:-4200}"
if ! [[ "$PIPELINE" =~ ^[A-Za-z0-9.@_-]+$ && "$DIGEST" =~ ^sha256:[0-9a-f]{64}$ && "$SINCE_EPOCH" =~ ^[0-9]+$ && "$POLL_INTERVAL" =~ ^[1-9][0-9]*$ && "$OVERALL_TIMEOUT" =~ ^[1-9][0-9]*$ ]]; then
echo 'ERROR: invalid pipeline, digest, epoch, or polling budget' >&2
exit 1
fi
export AWS_PAGER=''
export AWS_RETRY_MODE=standard
export AWS_MAX_ATTEMPTS=3

deadline=$(( $(date +%s) + OVERALL_TIMEOUT ))
log() { echo "[wait-for-ecs-cutover] $*"; }
check_deadline() {
if [ "$(date +%s)" -ge "$deadline" ]; then
log "ERROR: timed out after ${OVERALL_TIMEOUT}s waiting for $1"
exit 1
fi
}
aws_read() {
aws --cli-connect-timeout 10 --cli-read-timeout 30 "$@"
}

find_execution() {
local executions
executions=$(aws_read codepipeline list-pipeline-executions \
--pipeline-name "$PIPELINE" --max-items 30 \
--query 'pipelineExecutionSummaries' --output json)
printf '%s\n' "$executions" | SINCE="$SINCE_EPOCH" DIGEST="$DIGEST" python3 -c '
import datetime, json, os, sys
since = int(os.environ["SINCE"])
def epoch(execution):
value = execution["startTime"]
if isinstance(value, (int, float)):
return value
return datetime.datetime.fromisoformat(value.replace("Z", "+00:00")).timestamp()
def matches(execution):
return any(r["actionName"] == "ECR_Source" and r.get("revisionId") == os.environ["DIGEST"] for r in execution.get("sourceRevisions", []))
executions = sorted(json.load(sys.stdin), key=epoch, reverse=True)
if since == 0:
if not executions or not matches(executions[0]):
raise SystemExit("ERROR: unchanged app tag does not match the latest pipeline execution; cutover is unverified")
selected = executions[0]
else:
selected = executions[0] if executions and epoch(executions[0]) >= since else None
if selected and not matches(selected):
raise SystemExit("ERROR: latest pipeline execution does not match this app digest; deployment was superseded or its source is unverified")
print(selected["pipelineExecutionId"] if selected else "")
'
}

EXECUTION_ID=''
while [ -z "$EXECUTION_ID" ]; do
check_deadline 'the matching pipeline execution'
EXECUTION_ID=$(find_execution)
if [ -z "$EXECUTION_ID" ]; then
log 'No matching execution since this push; waiting'
sleep "$POLL_INTERVAL"
fi
done
log "Matched pipeline execution: $EXECUTION_ID"

DEPLOYMENT_ID=''
while [ -z "$DEPLOYMENT_ID" ] || [ "$DEPLOYMENT_ID" = 'None' ]; do
check_deadline 'the CodeDeploy deployment (the Deploy stage may be queued)'
status=$(aws_read codepipeline get-pipeline-execution \
--pipeline-name "$PIPELINE" --pipeline-execution-id "$EXECUTION_ID" \
--query 'pipelineExecution.status' --output text)
case "$status" in
Failed|Stopped|Stopping|Superseded|Cancelled)
log "ERROR: pipeline execution ended in $status; not promoting"; exit 1 ;;
InProgress|Succeeded) ;;
*) log "ERROR: unexpected pipeline status: $status"; exit 1 ;;
esac
# Action history does not publish the external deployment ID until cleanup
# finishes. Live state exposes it while traffic is shifting. Correlate both
# the stage execution and action attempt so old state cannot satisfy this run.
deploy_state=$(aws_read codepipeline get-pipeline-state --name "$PIPELINE" \
--query "stageStates[?stageName=='Deploy'] | [0]" --output json)
deploy_actions=$(aws_read codepipeline list-action-executions \
--pipeline-name "$PIPELINE" --filter pipelineExecutionId="$EXECUTION_ID" \
--query "actionExecutionDetails[?stageName=='Deploy']" --output json)
DEPLOYMENT_ID=$(printf '%s\n' "$deploy_actions" | DEPLOY_STATE="$deploy_state" EXECUTION_ID="$EXECUTION_ID" python3 -c '
import json, os, re, sys
state = json.loads(os.environ["DEPLOY_STATE"])
actions = json.load(sys.stdin)
if not state or state.get("latestExecution", {}).get("pipelineExecutionId") != os.environ["EXECUTION_ID"] or not actions:
print("")
sys.exit(0)
if len({a["actionName"] for a in actions}) != 1:
raise SystemExit("ERROR: expected one Deploy action in the app pipeline")
latest = max(actions, key=lambda a: a["startTime"])
matches = [a["latestExecution"] for a in state.get("actionStates", [])
if a["actionName"] == latest["actionName"]
and a.get("latestExecution", {}).get("actionExecutionId") == latest["actionExecutionId"]]
if len(matches) > 1:
raise SystemExit("ERROR: ambiguous live Deploy action")
if not matches:
print("")
sys.exit(0)
if latest["status"] not in ("InProgress", "Succeeded"):
raise SystemExit("ERROR: Deploy action ended in " + latest["status"])
deployment_id = matches[0].get("externalExecutionId", "")
if deployment_id and not re.fullmatch(r"d-[A-Za-z0-9]+", deployment_id):
raise SystemExit("ERROR: invalid CodeDeploy deployment ID in pipeline state")
print(deployment_id)
')
if [ -z "$DEPLOYMENT_ID" ] || [ "$DEPLOYMENT_ID" = 'None' ]; then
if [ "$status" = 'Succeeded' ]; then
log 'ERROR: successful pipeline has no CodeDeploy deployment'; exit 1
fi
sleep "$POLL_INTERVAL"
fi
done
log "CodeDeploy deployment: $DEPLOYMENT_ID"

while true; do
check_deadline 'AllowTraffic on every ECS target'
status=$(aws_read deploy get-deployment --deployment-id "$DEPLOYMENT_ID" \
--query 'deploymentInfo.status' --output text)
case "$status" in
Failed|Stopped) log "ERROR: deployment ended in $status; not promoting"; exit 1 ;;
Created|Queued|InProgress|Baking|Ready|Succeeded) ;;
*) log "ERROR: unexpected deployment status: $status"; exit 1 ;;
esac
target_ids=$(aws_read deploy list-deployment-targets --deployment-id "$DEPLOYMENT_ID" \
--query 'targetIds' --output text)
if [ -n "$target_ids" ] && [ "$target_ids" != 'None' ]; then
all_ok=1
for target in $target_ids; do
cutover=$(aws_read deploy get-deployment-target --deployment-id "$DEPLOYMENT_ID" --target-id "$target" \
--query "deploymentTarget.ecsTarget.lifecycleEvents[?lifecycleEventName=='AllowTraffic'].status | [0]" \
--output text)
case "$cutover" in
Succeeded) ;;
Failed|Skipped|Unknown) log "ERROR: target $target cutover status $cutover"; exit 1 ;;
Pending|InProgress|None|'') all_ok=0 ;;
*) log "ERROR: unexpected cutover status: $cutover"; exit 1 ;;
esac
done
if [ "$all_ok" = 1 ]; then
LATEST_EXECUTION_ID=$(find_execution)
if [ "$LATEST_EXECUTION_ID" != "$EXECUTION_ID" ]; then
log 'ERROR: a newer pipeline execution appeared during cutover; not promoting'
exit 1
fi
log 'Traffic cutover complete on every ECS target'
exit 0
fi
fi
log 'Traffic cutover is not complete; waiting'
sleep "$POLL_INTERVAL"
done
Loading
Loading