factory-ai 1.4.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -4
- package/Dockerfile.worker +1 -1
- package/README.md +8 -2
- package/ROADMAP.md +0 -1
- package/bin/factory +118 -1
- package/bootstrap/agent-factory-worker.service +2 -2
- package/bootstrap/auto-update.sh +20 -12
- package/bootstrap/deploy-runtime.sh +5 -3
- package/bootstrap/factory-ai-container-firewall.service +14 -0
- package/bootstrap/factory-ai-snapshot.service +1 -0
- package/bootstrap/factory-ai-watchdog.service +21 -0
- package/bootstrap/factory-ai-watchdog.timer +11 -0
- package/bootstrap/setup.sh +20 -9
- package/cmd/factory-ui/main.go +455 -0
- package/go.mod +39 -0
- package/go.sum +82 -0
- package/package.json +7 -3
- package/scripts/test.js +7 -0
- package/src/acp-adapter.js +26 -0
- package/src/acp-cli.js +16 -0
- package/src/activity.js +96 -0
- package/src/agent-executor.js +39 -5
- package/src/agent-runner.js +30 -10
- package/src/approval-policy.js +12 -0
- package/src/azure-harness.js +47 -14
- package/src/bedrock-harness.js +45 -14
- package/src/config.js +9 -0
- package/src/container-runner.js +65 -12
- package/src/context-compaction.js +16 -0
- package/src/control-plane.js +77 -16
- package/src/control-service.js +6 -1
- package/src/dashboard.js +26 -6
- package/src/extension-cli.js +11 -0
- package/src/extension-manifest.js +76 -0
- package/src/hooks.js +62 -0
- package/src/instructions.js +45 -0
- package/src/mcp-tools.js +19 -3
- package/src/objective-status.js +16 -0
- package/src/operator-logs.js +24 -0
- package/src/operator.js +31 -9
- package/src/process.js +3 -3
- package/src/release-gate.js +1 -0
- package/src/release-service.js +4 -1
- package/src/repo-map.js +128 -0
- package/src/reporter.js +11 -0
- package/src/retriever.js +18 -18
- package/src/routing.js +11 -1
- package/src/scanner-suite.js +4 -3
- package/src/snapshot.js +16 -0
- package/src/task-entry.js +28 -6
- package/src/task-graph.js +0 -5
- package/src/telegram-service.js +7 -1
- package/src/telegram.js +5 -1
- package/src/telemetry.js +173 -0
- package/src/tui.js +27 -8
- package/src/validation.js +41 -9
- package/src/watchdog.js +62 -0
- package/src/worker.js +32 -3
- package/src/workspace-tools.js +13 -3
- package/src/workspace.js +22 -2
- package/templates/AGENTS.md +15 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,12 +4,30 @@ All notable changes follow semantic versioning and the Keep a Changelog structur
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.5.1] - 2026-07-13
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Exclude unfinished tasks from stale-agent health after their objective reaches a terminal state.
|
|
12
|
+
|
|
13
|
+
## [1.5.0] - 2026-07-13
|
|
14
|
+
|
|
7
15
|
### Added
|
|
8
16
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- Durable
|
|
17
|
+
- Cross-platform Bubble Tea/Lip Gloss operator client with direct Azure Blob snapshots, verified release binaries, scrolling, and safe Node action fallback.
|
|
18
|
+
- Automatic Azure and Bedrock context compaction with immutable objective retention and recent tool evidence.
|
|
19
|
+
- Hierarchical `AGENTS.md` discovery, preconfigured project instructions, and bounded `.agent-factory` context.
|
|
20
|
+
- Durable worktree crash recovery, parallel read-only tool batches, live activity timelines, retry/error telemetry, and stale-agent watchdog recovery.
|
|
21
|
+
- Post-setup `factory models show|set|reset` and `factory configure models` workflows with validated routes, atomic runtime rollback, and update-safe persistence.
|
|
22
|
+
- Open-source harness adoption research and executable Go CI gates.
|
|
23
|
+
- Token-budgeted repository maps, safe OpenTelemetry GenAI records, Promptfoo/Inspect/Harbor evaluation contracts, typed lifecycle hooks, durable approval interrupts, optional ACP intake, and signed extension verification.
|
|
24
|
+
|
|
25
|
+
### Security
|
|
26
|
+
|
|
27
|
+
- Read-only role filesystems and command policy, deterministic pre-push Gitleaks enforcement, typed scanner release blockers, terminal-safe Go rendering, and structured allowlisted operator logs.
|
|
28
|
+
- Repository-scoped MCP memory; legacy unscoped memory is retained as `legacy-unscoped-knowledge-graph.jsonl` instead of being mixed into new repositories.
|
|
29
|
+
- Model credentials now enter agent containers through a consumed stdin envelope rather than environment variables; a persistent Docker firewall blocks cloud metadata access.
|
|
30
|
+
- Release publishing now binds tags to package versions, requires full gates and npm provenance, and attests immutable Go binaries.
|
|
13
31
|
|
|
14
32
|
## [1.4.0] - 2026-07-13
|
|
15
33
|
|
package/Dockerfile.worker
CHANGED
package/README.md
CHANGED
|
@@ -73,7 +73,13 @@ factory ui
|
|
|
73
73
|
| `factory issue OWNER/REPO NUMBER` | Turn a GitHub issue into a tracked objective |
|
|
74
74
|
| `factory telegram configure` | Configure allowlisted Telegram remote intake |
|
|
75
75
|
| `factory dashboard` | Objectives, agents, models, queue, DLQ, and Azure cost |
|
|
76
|
-
| `factory init PATH` | Create durable repo-local project context |
|
|
76
|
+
| `factory init PATH` | Create `AGENTS.md` and durable repo-local project context without overwriting existing files |
|
|
77
|
+
| `factory models show` | Show effective role-to-model routes |
|
|
78
|
+
| `factory models set ROLE PROVIDER/MODEL` | Validate and atomically switch a role to a new model |
|
|
79
|
+
| `factory models reset ROLE` | Restore a role to the versioned default |
|
|
80
|
+
| `factory configure models` | Interactively change model routing after setup |
|
|
81
|
+
| `factory acp REQUEST.json` | Submit a strictly validated optional ACP objective |
|
|
82
|
+
| `factory extension verify MANIFEST ARTIFACT PUBLIC_KEY` | Verify a signed extension manifest and immutable artifact digest |
|
|
77
83
|
| `factory doctor` | Services, storage, memory, and host health |
|
|
78
84
|
| `factory queue` | Queue and dead-letter depth |
|
|
79
85
|
| `factory logs` | Consolidated service logs |
|
|
@@ -157,7 +163,7 @@ Every repository gets two memory layers:
|
|
|
157
163
|
|
|
158
164
|
Built-in progressive skills include `/goal`, `/loop`, project context, systematic debugging, TDD, verification, security review, dependency security, browser verification, release discipline, and token efficiency. Context7 and Playwright MCPs are pinned and role-scoped.
|
|
159
165
|
|
|
160
|
-
Use `factory init PATH` to create `.agent-factory/` project, architecture, commands, decisions, risks, and handoff files without overwriting existing context.
|
|
166
|
+
Use `factory init PATH` to create a safe root `AGENTS.md` plus `.agent-factory/` project, architecture, commands, decisions, risks, and handoff files without overwriting existing context. The runtime discovers repository `AGENTS.md` instructions for planners and workers. Active Azure and Bedrock conversations compact automatically after their configured token threshold while preserving bounded recent tool evidence.
|
|
161
167
|
|
|
162
168
|
## Credentials
|
|
163
169
|
|
package/ROADMAP.md
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
- Evaluation Lab with model promotion and rollback evidence.
|
|
6
6
|
- Enhanced TUI timeline, diffs, files, checks, and safe takeover leases.
|
|
7
|
-
- Telegram remote objective status and completion notifications.
|
|
8
7
|
- Multi-repository objective schema and release groups.
|
|
9
8
|
|
|
10
9
|
## Platform
|
package/bin/factory
CHANGED
|
@@ -23,6 +23,9 @@ usage() {
|
|
|
23
23
|
'Factory AI operator console' \
|
|
24
24
|
'Usage: factory <command>' \
|
|
25
25
|
' setup' \
|
|
26
|
+
' configure models' \
|
|
27
|
+
' models show | set ROLE PROVIDER/MODEL | reset ROLE' \
|
|
28
|
+
' acp REQUEST.json | extension verify MANIFEST ARTIFACT PUBLIC_KEY' \
|
|
26
29
|
' github status | connect ORG | transfer OWNER/REPO ORG' \
|
|
27
30
|
' telegram status | configure | start | stop' \
|
|
28
31
|
' submit <owner/repo> <objective>' \
|
|
@@ -36,7 +39,97 @@ usage() {
|
|
|
36
39
|
|
|
37
40
|
command=${1:-ui}
|
|
38
41
|
shift || true
|
|
42
|
+
|
|
43
|
+
apply_runtime_model() {
|
|
44
|
+
local variable=$1 value=${2:-} mode=${3:-set} script settings
|
|
45
|
+
script="set -euo pipefail; exec 9>/run/lock/factory-ai-config.lock; flock -x 9; first=/etc/agent-factory.env; second=/etc/agent-factory-control.env; backup_first=\$(mktemp); backup_second=\$(mktemp); cp --preserve=mode,ownership \"\$first\" \"\$backup_first\"; cp --preserve=mode,ownership \"\$second\" \"\$backup_second\"; rollback() { install -m 0600 -o root -g root \"\$backup_first\" \"\$first\"; install -m 0600 -o root -g root \"\$backup_second\" \"\$second\"; systemctl restart agent-factory-control agent-factory-worker agent-factory-release || true; rm -f \"\$backup_first\" \"\$backup_second\"; }; trap rollback ERR TERM INT; for file in \"\$first\" \"\$second\"; do tmp=\$(mktemp); grep -v '^${variable}=' \"\$file\" > \"\$tmp\" || true; if [[ '$mode' == set ]]; then printf '${variable}=%s\\n' '$value' >> \"\$tmp\"; fi; install -m 0600 -o root -g root \"\$tmp\" \"\$file\"; rm -f \"\$tmp\"; done; systemctl restart agent-factory-control agent-factory-worker agent-factory-release; systemctl is-active --quiet agent-factory-control agent-factory-worker agent-factory-release; trap - ERR TERM INT; rm -f \"\$backup_first\" \"\$backup_second\""
|
|
46
|
+
printf -v script 'bash -c %q' "$script"
|
|
47
|
+
settings=$(jq -n --arg command "$script" '{commandToExecute:$command}')
|
|
48
|
+
az vm extension set --resource-group "$RESOURCE_GROUP" --vm-name "$VM" --name FactoryAIConfigure \
|
|
49
|
+
--publisher Microsoft.Azure.Extensions --version 2.1 --force-update --protected-settings "$settings" --output none
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
update_local_model() {
|
|
53
|
+
local variable=$1 value=${2:-} mode=${3:-set} temporary
|
|
54
|
+
mkdir -p "$(dirname "$CONFIG_FILE")"
|
|
55
|
+
temporary=$(mktemp "${CONFIG_FILE}.XXXXXX")
|
|
56
|
+
if [[ -f $CONFIG_FILE ]]; then grep -v "^${variable}=" "$CONFIG_FILE" > "$temporary" || true
|
|
57
|
+
elif [[ -f $LEGACY_CONFIG_FILE ]]; then grep -v "^${variable}=" "$LEGACY_CONFIG_FILE" > "$temporary" || true; fi
|
|
58
|
+
if [[ $mode == set ]]; then printf '%s=%q\n' "$variable" "$value" >> "$temporary"; fi
|
|
59
|
+
chmod 0600 "$temporary"
|
|
60
|
+
mv "$temporary" "$CONFIG_FILE"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
configure_model_transaction() {
|
|
64
|
+
local variable=$1 value=${2:-} mode=${3:-set}
|
|
65
|
+
MODEL_CONFIG_BACKUP=$(mktemp)
|
|
66
|
+
MODEL_CONFIG_EXISTED=false
|
|
67
|
+
if [[ -f $CONFIG_FILE ]]; then cp "$CONFIG_FILE" "$MODEL_CONFIG_BACKUP"; MODEL_CONFIG_EXISTED=true; fi
|
|
68
|
+
rollback_local_model() {
|
|
69
|
+
if [[ $MODEL_CONFIG_EXISTED == true ]]; then mv "$MODEL_CONFIG_BACKUP" "$CONFIG_FILE"; else rm -f "$CONFIG_FILE" "$MODEL_CONFIG_BACKUP"; fi
|
|
70
|
+
}
|
|
71
|
+
trap rollback_local_model EXIT INT TERM
|
|
72
|
+
update_local_model "$variable" "$value" "$mode"
|
|
73
|
+
apply_runtime_model "$variable" "$value" "$mode"
|
|
74
|
+
rm -f "$MODEL_CONFIG_BACKUP"
|
|
75
|
+
trap - EXIT INT TERM
|
|
76
|
+
}
|
|
77
|
+
|
|
39
78
|
case "$command" in
|
|
79
|
+
acp)
|
|
80
|
+
request=${1:?ACP request JSON file required}
|
|
81
|
+
SERVICE_BUS_NAMESPACE="$NAMESPACE" CONTROL_QUEUE=control-events FACTORY_ACP_ENABLED=true exec node "$FACTORY_ROOT/src/acp-cli.js" "$request"
|
|
82
|
+
;;
|
|
83
|
+
extension)
|
|
84
|
+
[[ ${1:-} == verify ]] || { printf 'Usage: factory extension verify MANIFEST ARTIFACT PUBLIC_KEY\n' >&2; exit 2; }
|
|
85
|
+
exec node "$FACTORY_ROOT/src/extension-cli.js" "${2:?manifest required}" "${3:?artifact required}" "${4:?public key required}"
|
|
86
|
+
;;
|
|
87
|
+
configure)
|
|
88
|
+
area=${1:-models}
|
|
89
|
+
[[ $area == models ]] || { printf 'Supported configuration area: models\n' >&2; exit 2; }
|
|
90
|
+
"$0" models show
|
|
91
|
+
read -r -p 'Role to change (or Enter to leave unchanged): ' role
|
|
92
|
+
[[ -n $role ]] || exit 0
|
|
93
|
+
read -r -p 'New provider/model route (or "default"): ' route
|
|
94
|
+
if [[ $route == default ]]; then exec "$0" models reset "$role"; fi
|
|
95
|
+
exec "$0" models set "$role" "$route"
|
|
96
|
+
;;
|
|
97
|
+
models)
|
|
98
|
+
action=${1:-show}
|
|
99
|
+
role=${2:-}
|
|
100
|
+
route=${3:-}
|
|
101
|
+
case "$action" in
|
|
102
|
+
show)
|
|
103
|
+
for current in scout planner builder tester debugger reviewer security release; do
|
|
104
|
+
upper=$(printf '%s' "$current" | tr '[:lower:]' '[:upper:]')
|
|
105
|
+
variable="FACTORY_MODEL_${upper}"
|
|
106
|
+
case "$current" in
|
|
107
|
+
scout) default=azureai-textved/factory-gpt-5-4-nano ;;
|
|
108
|
+
tester) default=azureai-responses/gpt-5.4 ;;
|
|
109
|
+
builder) default=azureai-textved/gpt-5.5 ;;
|
|
110
|
+
*) default=azureai-textved/gpt-5.6-sol ;;
|
|
111
|
+
esac
|
|
112
|
+
printf '%-10s %s%s\n' "$current" "${!variable:-$default}" "$([[ -n ${!variable:-} ]] && printf ' (override)' || true)"
|
|
113
|
+
done
|
|
114
|
+
;;
|
|
115
|
+
set)
|
|
116
|
+
[[ $role =~ ^(scout|planner|builder|tester|debugger|reviewer|security|release)$ ]] || { printf 'Invalid model role\n' >&2; exit 2; }
|
|
117
|
+
node --input-type=module -e "import { validateModelRoute } from 'file://$FACTORY_ROOT/src/routing.js'; validateModelRoute(process.argv[1])" "$route"
|
|
118
|
+
upper=$(printf '%s' "$role" | tr '[:lower:]' '[:upper:]')
|
|
119
|
+
variable="FACTORY_MODEL_${upper}"
|
|
120
|
+
configure_model_transaction "$variable" "$route" set
|
|
121
|
+
printf '%s now routes to %s. Runtime services are healthy.\n' "$role" "$route"
|
|
122
|
+
;;
|
|
123
|
+
reset)
|
|
124
|
+
[[ $role =~ ^(scout|planner|builder|tester|debugger|reviewer|security|release)$ ]] || { printf 'Invalid model role\n' >&2; exit 2; }
|
|
125
|
+
upper=$(printf '%s' "$role" | tr '[:lower:]' '[:upper:]')
|
|
126
|
+
variable="FACTORY_MODEL_${upper}"
|
|
127
|
+
configure_model_transaction "$variable" "" reset
|
|
128
|
+
printf '%s restored to the versioned default. Runtime services are healthy.\n' "$role"
|
|
129
|
+
;;
|
|
130
|
+
*) printf 'Usage: factory models show | set ROLE PROVIDER/MODEL | reset ROLE\n' >&2; exit 2 ;;
|
|
131
|
+
esac
|
|
132
|
+
;;
|
|
40
133
|
telegram)
|
|
41
134
|
action=${1:-status}
|
|
42
135
|
case "$action" in
|
|
@@ -80,6 +173,29 @@ case "$command" in
|
|
|
80
173
|
"$0" setup
|
|
81
174
|
exec "$0" ui
|
|
82
175
|
fi
|
|
176
|
+
version=$(node -p "require('$FACTORY_ROOT/package.json').version")
|
|
177
|
+
os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
|
178
|
+
arch=$(uname -m)
|
|
179
|
+
[[ $arch == x86_64 ]] && arch=amd64
|
|
180
|
+
[[ $arch == aarch64 ]] && arch=arm64
|
|
181
|
+
cache="$HOME/.cache/factory-ai/$version"
|
|
182
|
+
binary="$cache/factory-ui-$os-$arch"
|
|
183
|
+
if [[ ! -x $binary ]]; then
|
|
184
|
+
mkdir -p "$cache"
|
|
185
|
+
base="https://github.com/itsvedantkumar/factory-ai/releases/download/v$version"
|
|
186
|
+
if curl -fsSL "$base/factory-ui-$os-$arch" -o "$binary.tmp" && curl -fsSL "$base/checksums.txt" -o "$cache/checksums.txt"; then
|
|
187
|
+
expected=""
|
|
188
|
+
while read -r checksum asset; do [[ $asset == "factory-ui-$os-$arch" ]] && expected=$checksum; done < "$cache/checksums.txt"
|
|
189
|
+
actual=""
|
|
190
|
+
if command -v shasum >/dev/null; then actual=$(shasum -a 256 "$binary.tmp"); actual=${actual%% *};
|
|
191
|
+
elif command -v sha256sum >/dev/null; then actual=$(sha256sum "$binary.tmp"); actual=${actual%% *}; fi
|
|
192
|
+
if [[ -n $expected && $actual == "$expected" ]] && gh attestation verify "$binary.tmp" --repo itsvedantkumar/factory-ai >/dev/null 2>&1; then mv "$binary.tmp" "$binary"; chmod 0755 "$binary"; else rm -f "$binary.tmp"; fi
|
|
193
|
+
fi
|
|
194
|
+
fi
|
|
195
|
+
if [[ -x $binary ]]; then
|
|
196
|
+
FACTORY_STORAGE_ACCOUNT="$STORAGE" FACTORY_NAME="$FACTORY_NAME" FACTORY_PURPOSE="$FACTORY_PURPOSE" exec "$binary"
|
|
197
|
+
fi
|
|
198
|
+
printf 'Charm client unavailable for this release; using Node fallback.\n' >&2
|
|
83
199
|
FACTORY_RESOURCE_GROUP="$RESOURCE_GROUP" FACTORY_VM="$VM" FACTORY_SERVICE_BUS="$NAMESPACE" FACTORY_KEY_VAULT="$VAULT" FACTORY_STORAGE_ACCOUNT="$STORAGE" FACTORY_NAME="$FACTORY_NAME" FACTORY_PURPOSE="$FACTORY_PURPOSE" node "$FACTORY_ROOT/src/tui.js"
|
|
84
200
|
;;
|
|
85
201
|
setup)
|
|
@@ -164,7 +280,7 @@ case "$command" in
|
|
|
164
280
|
printf 'Azure foundation and credential vault ready. Config: %s\n' "$CONFIG_FILE"
|
|
165
281
|
if [[ $deploy_now == true ]]; then
|
|
166
282
|
source_ref=$(gh api repos/itsvedantkumar/factory-ai/commits/main --jq .sha)
|
|
167
|
-
parameters=(KEY_VAULT_NAME "$vault" SERVICE_BUS_NAMESPACE "$namespace"
|
|
283
|
+
parameters=(KEY_VAULT_NAME "$vault" SERVICE_BUS_NAMESPACE "$namespace" FACTORY_STORAGE_ACCOUNT "$storage" FACTORY_NAME "$factory_name" FACTORY_PURPOSE "$factory_purpose" SOURCE_REPOSITORY itsvedantkumar/factory-ai SOURCE_REF "$source_ref")
|
|
168
284
|
if [[ $provider == bedrock ]]; then
|
|
169
285
|
parameters+=(AWS_REGION "$aws_region")
|
|
170
286
|
for role in SCOUT PLANNER BUILDER TESTER DEBUGGER REVIEWER SECURITY RELEASE; do parameters+=("FACTORY_MODEL_$role" "bedrock/$bedrock_builder"); done
|
|
@@ -243,6 +359,7 @@ case "$command" in
|
|
|
243
359
|
[[ -d $target ]] || { printf 'Project directory does not exist: %s\n' "$target" >&2; exit 2; }
|
|
244
360
|
context="$target/.agent-factory"
|
|
245
361
|
mkdir -p "$context"
|
|
362
|
+
[[ -e $target/AGENTS.md ]] || cp "$FACTORY_ROOT/templates/AGENTS.md" "$target/AGENTS.md"
|
|
246
363
|
for template in "$FACTORY_ROOT/templates/project/"*.md; do
|
|
247
364
|
destination="$context/$(basename "$template")"
|
|
248
365
|
[[ -e $destination ]] || cp "$template" "$destination"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
[Unit]
|
|
2
2
|
Description=Factory AI durable worker
|
|
3
|
-
After=network-online.target docker.service factory-ai-qdrant.service factory-ai-ollama.service
|
|
3
|
+
After=network-online.target docker.service factory-ai-container-firewall.service factory-ai-qdrant.service factory-ai-ollama.service
|
|
4
4
|
Wants=network-online.target factory-ai-qdrant.service factory-ai-ollama.service
|
|
5
|
-
Requires=docker.service
|
|
5
|
+
Requires=docker.service factory-ai-container-firewall.service
|
|
6
6
|
StartLimitIntervalSec=300
|
|
7
7
|
StartLimitBurst=10
|
|
8
8
|
|
package/bootstrap/auto-update.sh
CHANGED
|
@@ -18,6 +18,12 @@ set -a
|
|
|
18
18
|
source /etc/agent-factory.env
|
|
19
19
|
set +a
|
|
20
20
|
github_token=$(az keyvault secret show --vault-name "$KEY_VAULT_NAME" --name github-token --query value --output tsv)
|
|
21
|
+
tag_commit=$(GH_TOKEN="$github_token" gh api "repos/itsvedantkumar/factory-ai/commits/v$version" --jq .sha)
|
|
22
|
+
[[ $tag_commit == "$commit" ]] || { printf 'npm gitHead does not match the immutable release tag.\n' >&2; exit 1; }
|
|
23
|
+
release_ok=$(GH_TOKEN="$github_token" gh release view "v$version" --repo itsvedantkumar/factory-ai --json isDraft,isPrerelease --jq '(.isDraft == false) and (.isPrerelease == false)')
|
|
24
|
+
[[ $release_ok == true ]] || { printf 'Matching stable GitHub release is unavailable.\n' >&2; exit 1; }
|
|
25
|
+
provenance=$(npm view "factory-ai@$version" dist.attestations.provenance.url 2>/dev/null || true)
|
|
26
|
+
[[ $provenance == https://search.sigstore.dev/* ]] || { printf 'npm release lacks verifiable provenance metadata.\n' >&2; exit 1; }
|
|
21
27
|
checks=$(GH_TOKEN="$github_token" gh api "repos/itsvedantkumar/factory-ai/commits/$commit/check-runs" --jq '[.check_runs[] | select(.name == "verify") | .conclusion] | any(. == "success")')
|
|
22
28
|
[[ $checks == true ]] || { printf 'Candidate commit lacks successful CI verification.\n' >&2; exit 1; }
|
|
23
29
|
|
|
@@ -41,20 +47,22 @@ docker run --rm --read-only --volume "$temporary/source:/workspace:ro" \
|
|
|
41
47
|
detect --source /workspace --redact --no-banner --exit-code 1
|
|
42
48
|
|
|
43
49
|
printf 'Deploying Factory AI %s (%s)\n' "$version" "$commit"
|
|
44
|
-
|
|
45
|
-
KEY_VAULT_NAME "$KEY_VAULT_NAME"
|
|
46
|
-
SERVICE_BUS_NAMESPACE "$SERVICE_BUS_NAMESPACE"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
deployment_parameters=(
|
|
51
|
+
KEY_VAULT_NAME "$KEY_VAULT_NAME"
|
|
52
|
+
SERVICE_BUS_NAMESPACE "$SERVICE_BUS_NAMESPACE"
|
|
53
|
+
SOURCE_REPOSITORY itsvedantkumar/factory-ai
|
|
54
|
+
SOURCE_REF "$commit"
|
|
55
|
+
)
|
|
56
|
+
for variable in FACTORY_STORAGE_ACCOUNT FACTORY_NAME FACTORY_PURPOSE AWS_REGION FACTORY_MODEL_SCOUT FACTORY_MODEL_PLANNER FACTORY_MODEL_BUILDER FACTORY_MODEL_TESTER FACTORY_MODEL_DEBUGGER FACTORY_MODEL_REVIEWER FACTORY_MODEL_SECURITY FACTORY_MODEL_RELEASE FACTORY_COMPACT_AFTER_INPUT_TOKENS FACTORY_COMPACT_MAX_CHARACTERS FACTORY_WATCHDOG_STALE_SECONDS FACTORY_HOOKS_JSON; do
|
|
57
|
+
[[ -n ${!variable:-} ]] && deployment_parameters+=("$variable" "${!variable}")
|
|
58
|
+
done
|
|
59
|
+
if ! bash "$temporary/source/bootstrap/deploy-runtime.sh" "${deployment_parameters[@]}"; then
|
|
50
60
|
printf 'Candidate deployment failed. Restoring previous runtime.\n' >&2
|
|
51
61
|
if [[ $previous_commit =~ ^[0-9a-f]{40}$ ]]; then
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
SOURCE_REPOSITORY itsvedantkumar/factory-ai \
|
|
57
|
-
SOURCE_REF "$previous_commit"
|
|
62
|
+
for ((index=0; index<${#deployment_parameters[@]}; index+=2)); do
|
|
63
|
+
[[ ${deployment_parameters[index]} == SOURCE_REF ]] && deployment_parameters[index+1]=$previous_commit
|
|
64
|
+
done
|
|
65
|
+
bash "$temporary/rollback-deploy.sh" "${deployment_parameters[@]}"
|
|
58
66
|
fi
|
|
59
67
|
exit 1
|
|
60
68
|
fi
|
|
@@ -20,7 +20,7 @@ while (($# > 0)); do
|
|
|
20
20
|
shift
|
|
21
21
|
fi
|
|
22
22
|
case "$name" in
|
|
23
|
-
KEY_VAULT_NAME|SERVICE_BUS_NAMESPACE|
|
|
23
|
+
KEY_VAULT_NAME|SERVICE_BUS_NAMESPACE|SOURCE_REPOSITORY|SOURCE_REF|FACTORY_STORAGE_ACCOUNT|FACTORY_NAME|FACTORY_PURPOSE|AWS_REGION|FACTORY_MODEL_SCOUT|FACTORY_MODEL_PLANNER|FACTORY_MODEL_BUILDER|FACTORY_MODEL_TESTER|FACTORY_MODEL_DEBUGGER|FACTORY_MODEL_REVIEWER|FACTORY_MODEL_SECURITY|FACTORY_MODEL_RELEASE|FACTORY_COMPACT_AFTER_INPUT_TOKENS|FACTORY_COMPACT_MAX_CHARACTERS|FACTORY_WATCHDOG_STALE_SECONDS|FACTORY_HOOKS_JSON) printf -v "$name" '%s' "$value" ;;
|
|
24
24
|
*) echo "Unknown deployment parameter: $name" >&2; exit 1 ;;
|
|
25
25
|
esac
|
|
26
26
|
done
|
|
@@ -29,7 +29,6 @@ done
|
|
|
29
29
|
: "${SERVICE_BUS_NAMESPACE:?SERVICE_BUS_NAMESPACE is required}"
|
|
30
30
|
: "${SOURCE_REPOSITORY:?SOURCE_REPOSITORY as OWNER/REPOSITORY is required}"
|
|
31
31
|
: "${SOURCE_REF:?SOURCE_REF must be a full commit SHA}"
|
|
32
|
-
SERVICE_BUS_QUEUE=${SERVICE_BUS_QUEUE:-code-tasks}
|
|
33
32
|
[[ $SOURCE_REPOSITORY =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] || { echo "Invalid SOURCE_REPOSITORY" >&2; exit 1; }
|
|
34
33
|
[[ $SOURCE_REF =~ ^[0-9a-f]{40}$ ]] || { echo "SOURCE_REF must be a full lowercase commit SHA" >&2; exit 1; }
|
|
35
34
|
|
|
@@ -60,7 +59,6 @@ rsync -a --delete --exclude .git "$temporary/source/" /opt/agent-factory/app/
|
|
|
60
59
|
|
|
61
60
|
KEY_VAULT_NAME="$KEY_VAULT_NAME" \
|
|
62
61
|
SERVICE_BUS_NAMESPACE="$SERVICE_BUS_NAMESPACE" \
|
|
63
|
-
SERVICE_BUS_QUEUE="$SERVICE_BUS_QUEUE" \
|
|
64
62
|
FACTORY_WORKER_IMAGE="agent-factory-worker:$SOURCE_REF" \
|
|
65
63
|
FACTORY_STORAGE_ACCOUNT="${FACTORY_STORAGE_ACCOUNT:-}" \
|
|
66
64
|
FACTORY_NAME="${FACTORY_NAME:-Factory AI}" \
|
|
@@ -74,6 +72,10 @@ FACTORY_MODEL_DEBUGGER="${FACTORY_MODEL_DEBUGGER:-}" \
|
|
|
74
72
|
FACTORY_MODEL_REVIEWER="${FACTORY_MODEL_REVIEWER:-}" \
|
|
75
73
|
FACTORY_MODEL_SECURITY="${FACTORY_MODEL_SECURITY:-}" \
|
|
76
74
|
FACTORY_MODEL_RELEASE="${FACTORY_MODEL_RELEASE:-}" \
|
|
75
|
+
FACTORY_COMPACT_AFTER_INPUT_TOKENS="${FACTORY_COMPACT_AFTER_INPUT_TOKENS:-80000}" \
|
|
76
|
+
FACTORY_COMPACT_MAX_CHARACTERS="${FACTORY_COMPACT_MAX_CHARACTERS:-24000}" \
|
|
77
|
+
FACTORY_WATCHDOG_STALE_SECONDS="${FACTORY_WATCHDOG_STALE_SECONDS:-900}" \
|
|
78
|
+
FACTORY_HOOKS_JSON="${FACTORY_HOOKS_JSON:-[]}" \
|
|
77
79
|
bash /opt/agent-factory/app/bootstrap/setup.sh
|
|
78
80
|
install -d -o factory -g factory -m 0750 /opt/agent-factory/state
|
|
79
81
|
version=$(node -p 'require("/opt/agent-factory/app/package.json").version')
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Block cloud metadata access from Factory AI containers
|
|
3
|
+
After=docker.service
|
|
4
|
+
Before=agent-factory-worker.service
|
|
5
|
+
Requires=docker.service
|
|
6
|
+
|
|
7
|
+
[Service]
|
|
8
|
+
Type=oneshot
|
|
9
|
+
ExecStart=/bin/bash -c '/usr/sbin/iptables -C DOCKER-USER -d 169.254.169.254/32 -j REJECT 2>/dev/null || /usr/sbin/iptables -I DOCKER-USER -d 169.254.169.254/32 -j REJECT'
|
|
10
|
+
RemainAfterExit=yes
|
|
11
|
+
NoNewPrivileges=true
|
|
12
|
+
|
|
13
|
+
[Install]
|
|
14
|
+
WantedBy=multi-user.target
|
|
@@ -7,6 +7,7 @@ Wants=network-online.target
|
|
|
7
7
|
Type=oneshot
|
|
8
8
|
User=factory
|
|
9
9
|
Group=factory
|
|
10
|
+
SupplementaryGroups=systemd-journal
|
|
10
11
|
WorkingDirectory=/opt/agent-factory/app
|
|
11
12
|
EnvironmentFile=/etc/agent-factory-control.env
|
|
12
13
|
ExecStart=/usr/bin/node /opt/agent-factory/app/src/snapshot.js
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Recover stale Factory AI agent containers
|
|
3
|
+
After=network-online.target docker.service agent-factory-control.service
|
|
4
|
+
Wants=network-online.target
|
|
5
|
+
Requires=docker.service
|
|
6
|
+
|
|
7
|
+
[Service]
|
|
8
|
+
Type=oneshot
|
|
9
|
+
User=factory
|
|
10
|
+
Group=factory
|
|
11
|
+
SupplementaryGroups=docker
|
|
12
|
+
WorkingDirectory=/opt/agent-factory/app
|
|
13
|
+
EnvironmentFile=/etc/agent-factory.env
|
|
14
|
+
ExecStart=/usr/bin/node /opt/agent-factory/app/src/watchdog.js
|
|
15
|
+
NoNewPrivileges=true
|
|
16
|
+
PrivateTmp=true
|
|
17
|
+
ProtectSystem=strict
|
|
18
|
+
ProtectHome=true
|
|
19
|
+
ReadWritePaths=/opt/agent-factory/state /opt/agent-factory/workspaces
|
|
20
|
+
UMask=0027
|
|
21
|
+
SyslogIdentifier=factory-ai-watchdog
|
package/bootstrap/setup.sh
CHANGED
|
@@ -10,7 +10,6 @@ fi
|
|
|
10
10
|
: "${KEY_VAULT_NAME:?Set KEY_VAULT_NAME to the existing vault name}"
|
|
11
11
|
: "${SERVICE_BUS_NAMESPACE:?Set SERVICE_BUS_NAMESPACE to the existing namespace name}"
|
|
12
12
|
: "${FACTORY_WORKER_IMAGE:?Set FACTORY_WORKER_IMAGE to an immutable image tag}"
|
|
13
|
-
SERVICE_BUS_QUEUE=${SERVICE_BUS_QUEUE:-code-tasks}
|
|
14
13
|
APP_DIR=${APP_DIR:-/opt/agent-factory/app}
|
|
15
14
|
FACTORY_USER=${FACTORY_USER:-factory}
|
|
16
15
|
|
|
@@ -18,11 +17,11 @@ chmod 0755 /opt/agent-factory "$APP_DIR"
|
|
|
18
17
|
|
|
19
18
|
export DEBIAN_FRONTEND=noninteractive
|
|
20
19
|
apt-get update
|
|
21
|
-
apt-get install -y ca-certificates curl gpg git jq
|
|
20
|
+
apt-get install -y ca-certificates curl gpg git jq iptables
|
|
22
21
|
|
|
23
22
|
install -d -m 0755 /etc/apt/keyrings
|
|
24
23
|
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor --yes -o /etc/apt/keyrings/nodesource.gpg
|
|
25
|
-
printf '%s\n' 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/
|
|
24
|
+
printf '%s\n' 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main' > /etc/apt/sources.list.d/nodesource.list
|
|
26
25
|
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg -o /etc/apt/keyrings/githubcli-archive-keyring.gpg
|
|
27
26
|
chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg
|
|
28
27
|
printf '%s\n' "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list
|
|
@@ -30,7 +29,7 @@ curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor --y
|
|
|
30
29
|
printf '%s\n' "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/microsoft.gpg] https://packages.microsoft.com/repos/azure-cli/ $(. /etc/os-release && echo "$VERSION_CODENAME") main" > /etc/apt/sources.list.d/azure-cli.list
|
|
31
30
|
apt-get update
|
|
32
31
|
apt-get install -y azure-cli gh nodejs
|
|
33
|
-
test "$(node --version | cut -d. -f1)" = "
|
|
32
|
+
test "$(node --version | cut -d. -f1)" = "v22"
|
|
34
33
|
|
|
35
34
|
id "$FACTORY_USER" >/dev/null 2>&1 || useradd --system --create-home --shell /usr/sbin/nologin "$FACTORY_USER"
|
|
36
35
|
test -f "$APP_DIR/package-lock.json"
|
|
@@ -46,6 +45,7 @@ for scanner_image in \
|
|
|
46
45
|
done
|
|
47
46
|
docker pull ollama/ollama@sha256:3d8a05e3432d50ea57594fabe971e46cc8fe963a0f9f8c40400bd56cd5388e47
|
|
48
47
|
docker pull qdrant/qdrant@sha256:31407c0e8e32eb771b71718f1a4772e2ad47a07557917b21ac96792f40eb8007
|
|
48
|
+
iptables -C DOCKER-USER -d 169.254.169.254/32 -j REJECT 2>/dev/null || iptables -I DOCKER-USER -d 169.254.169.254/32 -j REJECT
|
|
49
49
|
|
|
50
50
|
STATE_DEVICE=/dev/disk/azure/scsi1/lun0
|
|
51
51
|
if [[ -b $STATE_DEVICE ]]; then
|
|
@@ -68,6 +68,9 @@ for secret_name in "${GITHUB_TOKEN_SECRET:-github-token}"; do
|
|
|
68
68
|
done
|
|
69
69
|
|
|
70
70
|
install -d -o "$FACTORY_USER" -g "$FACTORY_USER" -m 0750 /opt/agent-factory/state /opt/agent-factory/state/home /opt/agent-factory/state/memory /opt/agent-factory/state/telegram /opt/agent-factory/state/retrieval /opt/agent-factory/workspaces /opt/agent-factory/logs
|
|
71
|
+
if [[ -f /opt/agent-factory/state/memory/knowledge-graph.jsonl && ! -e /opt/agent-factory/state/memory/legacy-unscoped-knowledge-graph.jsonl ]]; then
|
|
72
|
+
mv /opt/agent-factory/state/memory/knowledge-graph.jsonl /opt/agent-factory/state/memory/legacy-unscoped-knowledge-graph.jsonl
|
|
73
|
+
fi
|
|
71
74
|
install -d -o root -g root -m 0750 /opt/agent-factory/state/qdrant /opt/agent-factory/state/qdrant-snapshots /opt/agent-factory/state/ollama
|
|
72
75
|
install -m 0600 -o root -g root /dev/null /etc/agent-factory.env
|
|
73
76
|
{
|
|
@@ -88,10 +91,11 @@ install -m 0600 -o root -g root /dev/null /etc/agent-factory.env
|
|
|
88
91
|
printf 'FACTORY_PURPOSE=%s\n' "${FACTORY_PURPOSE:-Ship secure reviewed software continuously}"
|
|
89
92
|
printf 'MAX_CONCURRENCY=3\n'
|
|
90
93
|
printf 'TASK_TIMEOUT_MS=1800000\n'
|
|
94
|
+
printf 'FACTORY_COMPACT_AFTER_INPUT_TOKENS=%s\n' "${FACTORY_COMPACT_AFTER_INPUT_TOKENS:-80000}"
|
|
95
|
+
printf 'FACTORY_COMPACT_MAX_CHARACTERS=%s\n' "${FACTORY_COMPACT_MAX_CHARACTERS:-24000}"
|
|
96
|
+
printf 'FACTORY_WATCHDOG_STALE_SECONDS=%s\n' "${FACTORY_WATCHDOG_STALE_SECONDS:-900}"
|
|
97
|
+
printf 'FACTORY_HOOKS_JSON=%s\n' "${FACTORY_HOOKS_JSON:-[]}"
|
|
91
98
|
printf 'MAX_DELIVERY_COUNT=8\n'
|
|
92
|
-
printf 'FACTORY_VERSION=%s\n' "$factory_version"
|
|
93
|
-
printf 'FACTORY_NAME=%s\n' "${FACTORY_NAME:-Factory AI}"
|
|
94
|
-
printf 'FACTORY_PURPOSE=%s\n' "${FACTORY_PURPOSE:-Ship secure reviewed software continuously}"
|
|
95
99
|
[[ -n ${AWS_REGION:-} ]] && printf 'AWS_REGION=%s\nAWS_DEFAULT_REGION=%s\n' "$AWS_REGION" "$AWS_REGION"
|
|
96
100
|
for variable in FACTORY_MODEL_SCOUT FACTORY_MODEL_PLANNER FACTORY_MODEL_BUILDER FACTORY_MODEL_TESTER FACTORY_MODEL_DEBUGGER FACTORY_MODEL_REVIEWER FACTORY_MODEL_SECURITY FACTORY_MODEL_RELEASE; do
|
|
97
101
|
[[ -n ${!variable:-} ]] && printf '%s=%s\n' "$variable" "${!variable}"
|
|
@@ -111,6 +115,8 @@ install -m 0600 -o root -g root /dev/null /etc/agent-factory-control.env
|
|
|
111
115
|
printf 'FACTORY_STATE_DIR=/opt/agent-factory/state\n'
|
|
112
116
|
printf 'FACTORY_REGISTRY=%s/config/capabilities.json\n' "$APP_DIR"
|
|
113
117
|
printf 'MAX_DELIVERY_COUNT=8\n'
|
|
118
|
+
printf 'FACTORY_COMPACT_AFTER_INPUT_TOKENS=%s\n' "${FACTORY_COMPACT_AFTER_INPUT_TOKENS:-80000}"
|
|
119
|
+
printf 'FACTORY_COMPACT_MAX_CHARACTERS=%s\n' "${FACTORY_COMPACT_MAX_CHARACTERS:-24000}"
|
|
114
120
|
[[ -n ${AWS_REGION:-} ]] && printf 'AWS_REGION=%s\nAWS_DEFAULT_REGION=%s\n' "$AWS_REGION" "$AWS_REGION"
|
|
115
121
|
for variable in FACTORY_MODEL_SCOUT FACTORY_MODEL_PLANNER FACTORY_MODEL_BUILDER FACTORY_MODEL_TESTER FACTORY_MODEL_DEBUGGER FACTORY_MODEL_REVIEWER FACTORY_MODEL_SECURITY FACTORY_MODEL_RELEASE; do
|
|
116
122
|
[[ -n ${!variable:-} ]] && printf '%s=%s\n' "$variable" "${!variable}"
|
|
@@ -123,6 +129,7 @@ chmod 0750 /opt/agent-factory/state/qdrant /opt/agent-factory/state/qdrant-snaps
|
|
|
123
129
|
chown -R root:root "$APP_DIR"
|
|
124
130
|
chmod -R go-w "$APP_DIR"
|
|
125
131
|
install -m 0644 "$APP_DIR/bootstrap/agent-factory-worker.service" /etc/systemd/system/agent-factory-worker.service
|
|
132
|
+
install -m 0644 "$APP_DIR/bootstrap/factory-ai-container-firewall.service" /etc/systemd/system/factory-ai-container-firewall.service
|
|
126
133
|
install -m 0644 "$APP_DIR/bootstrap/agent-factory-control.service" /etc/systemd/system/agent-factory-control.service
|
|
127
134
|
install -m 0644 "$APP_DIR/bootstrap/agent-factory-release.service" /etc/systemd/system/agent-factory-release.service
|
|
128
135
|
install -m 0644 "$APP_DIR/bootstrap/agent-factory-reporter.service" /etc/systemd/system/agent-factory-reporter.service
|
|
@@ -132,17 +139,21 @@ chmod 0755 "$APP_DIR/bootstrap/auto-update.sh"
|
|
|
132
139
|
install -m 0644 "$APP_DIR/bootstrap/factory-ai-update.service" /etc/systemd/system/factory-ai-update.service
|
|
133
140
|
install -m 0644 "$APP_DIR/bootstrap/factory-ai-update.timer" /etc/systemd/system/factory-ai-update.timer
|
|
134
141
|
install -m 0644 "$APP_DIR/bootstrap/factory-ai-snapshot.service" /etc/systemd/system/factory-ai-snapshot.service
|
|
135
|
-
install -m 0644 "$APP_DIR/bootstrap/factory-ai-snapshot.timer" /etc/systemd/system/factory-ai-snapshot.timer
|
|
142
|
+
install -m 0644 "$APP_DIR/bootstrap/factory-ai-snapshot.timer" /etc/systemd/system/factory-ai-snapshot.timer
|
|
143
|
+
install -m 0644 "$APP_DIR/bootstrap/factory-ai-watchdog.service" /etc/systemd/system/factory-ai-watchdog.service
|
|
144
|
+
install -m 0644 "$APP_DIR/bootstrap/factory-ai-watchdog.timer" /etc/systemd/system/factory-ai-watchdog.timer
|
|
136
145
|
install -m 0644 "$APP_DIR/bootstrap/factory-ai-qdrant.service" /etc/systemd/system/factory-ai-qdrant.service
|
|
137
146
|
install -m 0644 "$APP_DIR/bootstrap/factory-ai-ollama.service" /etc/systemd/system/factory-ai-ollama.service
|
|
138
147
|
systemctl daemon-reload
|
|
148
|
+
systemctl enable --now factory-ai-container-firewall.service
|
|
139
149
|
systemctl enable --now agent-factory-worker.service
|
|
140
150
|
systemctl enable --now agent-factory-control.service
|
|
141
151
|
systemctl enable --now agent-factory-release.service
|
|
142
152
|
systemctl enable --now agent-factory-reporter.timer
|
|
143
153
|
systemctl enable --now agent-factory-telegram.service
|
|
144
154
|
systemctl enable --now factory-ai-update.timer
|
|
145
|
-
systemctl enable --now factory-ai-snapshot.timer
|
|
155
|
+
systemctl enable --now factory-ai-snapshot.timer
|
|
156
|
+
systemctl enable --now factory-ai-watchdog.timer
|
|
146
157
|
systemctl enable --now factory-ai-qdrant.service factory-ai-ollama.service
|
|
147
158
|
for _ in $(seq 1 60); do curl -fsS http://127.0.0.1:11434/api/tags >/dev/null && break; sleep 2; done
|
|
148
159
|
docker exec factory-ai-ollama ollama show embeddinggemma >/dev/null 2>&1 || docker exec factory-ai-ollama ollama pull embeddinggemma
|