Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ new version heading in the same commit.

## [Unreleased]

## [0.448.6] - 2026-09-22
### Fixed
- **An agent's git commands can no longer reach the Agentric checkout.** An agent's folder usually isn't a
repo, so git walks up, and where the data home sits inside the product checkout it lands in agent-os
itself. On globex an engineer run did `cd <a work dir that no longer existed>` followed, unchained, by
`git remote set-url origin …client-app` + `git reset --hard`. The cd failed, and the product's own source
was replaced by another repo (`terminal/` gone, so every later session crashed on launch, for 80 minutes).
Every launch now exports `GIT_CEILING_DIRECTORIES` = the agents folder. Repos inside an agent's folder
are unaffected. Pinned by `scripts/git-ceiling-test.cjs`.

## [0.448.5] - 2026-09-22
### Fixed
- **Two synchronous paths could freeze a whole tenant's server.** Both hit globex on 2026-09-22: `/health`
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agent-os",
"version": "0.448.5",
"version": "0.448.6",
"description": "A generic, governed operating system for running autonomous agents safely across brands. Ships with a local web console.",
"license": "MIT",
"type": "commonjs",
Expand All @@ -27,7 +27,7 @@
"check-deps": "bash scripts/install-deps.sh --check",
"dev": "ts-node src/cli.ts serve",
"demo:dev": "ts-node src/demo.ts",
"test:governance": "node scripts/version-sync-test.cjs && node scripts/event-loop-freeze-test.cjs && node scripts/governance-conformance.cjs && node scripts/tier-a-policy-test.cjs && node scripts/policy-baseline-test.cjs && node scripts/heredoc-intent-test.cjs && node scripts/capability-registry-test.cjs && node scripts/composio-envelope-test.cjs && node scripts/composio-identity-test.cjs && node scripts/idle-reaper-test.cjs && node scripts/dm-continuity-test.cjs && node scripts/telegram-dm-lane-test.cjs && node scripts/cli-link-origin-test.cjs && node scripts/alert-staleness-test.cjs && node scripts/run-as-identity-test.cjs && node scripts/email-identity-guard-test.cjs && node scripts/deps-freshness-test.cjs && node scripts/runtime-account-test.cjs && node scripts/runtime-account-misattribution-test.cjs && node scripts/runtime-usage-refresh-test.cjs && node scripts/keychain-credential-test.cjs && node scripts/credential-preflight-test.cjs && node scripts/capacity-queue-test.cjs && node scripts/runtime-login-test.cjs && node scripts/rotate-on-reload-test.cjs && node scripts/headless-resumable-test.cjs && node scripts/session-revive-gates-test.cjs && node scripts/session-pause-test.cjs && node scripts/make-live-lock-test.cjs && node scripts/claude-config-seed-test.cjs && node scripts/claude-config-isolation-test.cjs && node scripts/output-style-test.cjs && node scripts/session-cost-test.cjs && node scripts/chain-model-test.cjs && node scripts/task-workers-test.cjs && node scripts/tuning-patch-test.cjs && node scripts/task-runs-test.cjs && node scripts/task-pr-links-test.cjs && node scripts/task-draft-delete-test.cjs && node scripts/task-discussion-delivery-test.cjs && node scripts/task-resume-test.cjs && node scripts/task-unblock-test.cjs && node scripts/audience-session-access-test.cjs && node scripts/warm-chat-test.cjs && node scripts/poke-warm-caller-test.cjs && node scripts/wakeup-queue-test.cjs && node scripts/stranded-human-stop-test.cjs && node scripts/inject-submit-test.cjs && node scripts/blocked-routing-test.cjs && node scripts/self-dispatch-guard-test.cjs && node scripts/task-proposals-test.cjs && node scripts/npm-boundary-test.cjs && node scripts/agent-edit-guard-test.cjs && node scripts/per-agent-context-test.cjs && node scripts/goal-update-guard-test.cjs && node scripts/insights-signal-test.cjs && node scripts/outcome-derivation-test.cjs && node scripts/episode-quality-test.cjs && node scripts/memory-upkeep-test.cjs && node scripts/automem-health-test.cjs && node scripts/memory-store-switch-test.cjs && node scripts/memory-preload-test.cjs && node scripts/turn-lifecycle-test.cjs && node scripts/resume-seed-test.cjs && node scripts/outcome-vocabulary-test.cjs && node scripts/skill-presets-test.cjs && node scripts/skill-edit-proposal-test.cjs && node scripts/notify-hook-route-test.cjs && node scripts/review-notify-test.cjs && node scripts/turn-idle-background-guard-test.cjs && node scripts/waiting-brief-test.cjs && node scripts/runtime-death-alert-test.cjs && node scripts/github-per-member-test.cjs && node scripts/github-multi-org-test.cjs && node scripts/card-measurement-test.cjs && node scripts/scheduler-admission-test.cjs && node scripts/tick-liveness-test.cjs && node scripts/audit-mirror-test.cjs && node scripts/request-metrics-test.cjs && node scripts/tool-usage-test.cjs && node scripts/sessions-list-perf-test.cjs && node scripts/summarizer-degradation-test.cjs && node scripts/agent-history-scope-test.cjs && node scripts/webhook-ingress-test.cjs && node scripts/slack-content-filter-test.cjs && node scripts/slack-ingress-test.cjs && node scripts/discord-ingress-test.cjs && node scripts/chat-attachments-test.cjs && node scripts/clickup-task-bridge-test.cjs && node scripts/agentric-commands-test.cjs && node scripts/whats-new-test.cjs && node scripts/opencode-gate-test.cjs && node scripts/protected-path-guard-test.cjs && node scripts/attach-grace-test.cjs && node scripts/attach-file-liveness-test.cjs && node scripts/feed-smoke.cjs && node scripts/activity-classify-test.cjs && node scripts/goal-room-test.cjs && node scripts/secret-rotation-test.cjs && node scripts/update-watch-test.cjs && node scripts/runtime-update-watch-test.cjs && node scripts/setup-wizard-test.cjs && node scripts/md-pdf-test.cjs && node scripts/proposal-surfacing-test.cjs && node scripts/process-janitor-test.cjs && node scripts/detached-work-steer-test.cjs && node scripts/statusline-install-test.cjs && node scripts/docs-create-agent-test.cjs && node scripts/agent-stats-rollup-test.cjs && node scripts/task-discussion-rollup-test.cjs && node scripts/session-insights-stamp-test.cjs && node scripts/loop-stall-attribution-test.cjs && node scripts/session-progress-test.cjs && node scripts/drift-nudge-test.cjs && node scripts/goal-metric-review-test.cjs && node scripts/capability-gap-test.cjs && node scripts/workflow-proposal-test.cjs && node scripts/automation-edit-proposal-test.cjs && node scripts/app-card-close-test.cjs",
"test:governance": "node scripts/version-sync-test.cjs && node scripts/event-loop-freeze-test.cjs && node scripts/git-ceiling-test.cjs && node scripts/governance-conformance.cjs && node scripts/tier-a-policy-test.cjs && node scripts/policy-baseline-test.cjs && node scripts/heredoc-intent-test.cjs && node scripts/capability-registry-test.cjs && node scripts/composio-envelope-test.cjs && node scripts/composio-identity-test.cjs && node scripts/idle-reaper-test.cjs && node scripts/dm-continuity-test.cjs && node scripts/telegram-dm-lane-test.cjs && node scripts/cli-link-origin-test.cjs && node scripts/alert-staleness-test.cjs && node scripts/run-as-identity-test.cjs && node scripts/email-identity-guard-test.cjs && node scripts/deps-freshness-test.cjs && node scripts/runtime-account-test.cjs && node scripts/runtime-account-misattribution-test.cjs && node scripts/runtime-usage-refresh-test.cjs && node scripts/keychain-credential-test.cjs && node scripts/credential-preflight-test.cjs && node scripts/capacity-queue-test.cjs && node scripts/runtime-login-test.cjs && node scripts/rotate-on-reload-test.cjs && node scripts/headless-resumable-test.cjs && node scripts/session-revive-gates-test.cjs && node scripts/session-pause-test.cjs && node scripts/make-live-lock-test.cjs && node scripts/claude-config-seed-test.cjs && node scripts/claude-config-isolation-test.cjs && node scripts/output-style-test.cjs && node scripts/session-cost-test.cjs && node scripts/chain-model-test.cjs && node scripts/task-workers-test.cjs && node scripts/tuning-patch-test.cjs && node scripts/task-runs-test.cjs && node scripts/task-pr-links-test.cjs && node scripts/task-draft-delete-test.cjs && node scripts/task-discussion-delivery-test.cjs && node scripts/task-resume-test.cjs && node scripts/task-unblock-test.cjs && node scripts/audience-session-access-test.cjs && node scripts/warm-chat-test.cjs && node scripts/poke-warm-caller-test.cjs && node scripts/wakeup-queue-test.cjs && node scripts/stranded-human-stop-test.cjs && node scripts/inject-submit-test.cjs && node scripts/blocked-routing-test.cjs && node scripts/self-dispatch-guard-test.cjs && node scripts/task-proposals-test.cjs && node scripts/npm-boundary-test.cjs && node scripts/agent-edit-guard-test.cjs && node scripts/per-agent-context-test.cjs && node scripts/goal-update-guard-test.cjs && node scripts/insights-signal-test.cjs && node scripts/outcome-derivation-test.cjs && node scripts/episode-quality-test.cjs && node scripts/memory-upkeep-test.cjs && node scripts/automem-health-test.cjs && node scripts/memory-store-switch-test.cjs && node scripts/memory-preload-test.cjs && node scripts/turn-lifecycle-test.cjs && node scripts/resume-seed-test.cjs && node scripts/outcome-vocabulary-test.cjs && node scripts/skill-presets-test.cjs && node scripts/skill-edit-proposal-test.cjs && node scripts/notify-hook-route-test.cjs && node scripts/review-notify-test.cjs && node scripts/turn-idle-background-guard-test.cjs && node scripts/waiting-brief-test.cjs && node scripts/runtime-death-alert-test.cjs && node scripts/github-per-member-test.cjs && node scripts/github-multi-org-test.cjs && node scripts/card-measurement-test.cjs && node scripts/scheduler-admission-test.cjs && node scripts/tick-liveness-test.cjs && node scripts/audit-mirror-test.cjs && node scripts/request-metrics-test.cjs && node scripts/tool-usage-test.cjs && node scripts/sessions-list-perf-test.cjs && node scripts/summarizer-degradation-test.cjs && node scripts/agent-history-scope-test.cjs && node scripts/webhook-ingress-test.cjs && node scripts/slack-content-filter-test.cjs && node scripts/slack-ingress-test.cjs && node scripts/discord-ingress-test.cjs && node scripts/chat-attachments-test.cjs && node scripts/clickup-task-bridge-test.cjs && node scripts/agentric-commands-test.cjs && node scripts/whats-new-test.cjs && node scripts/opencode-gate-test.cjs && node scripts/protected-path-guard-test.cjs && node scripts/attach-grace-test.cjs && node scripts/attach-file-liveness-test.cjs && node scripts/feed-smoke.cjs && node scripts/activity-classify-test.cjs && node scripts/goal-room-test.cjs && node scripts/secret-rotation-test.cjs && node scripts/update-watch-test.cjs && node scripts/runtime-update-watch-test.cjs && node scripts/setup-wizard-test.cjs && node scripts/md-pdf-test.cjs && node scripts/proposal-surfacing-test.cjs && node scripts/process-janitor-test.cjs && node scripts/detached-work-steer-test.cjs && node scripts/statusline-install-test.cjs && node scripts/docs-create-agent-test.cjs && node scripts/agent-stats-rollup-test.cjs && node scripts/task-discussion-rollup-test.cjs && node scripts/session-insights-stamp-test.cjs && node scripts/loop-stall-attribution-test.cjs && node scripts/session-progress-test.cjs && node scripts/drift-nudge-test.cjs && node scripts/goal-metric-review-test.cjs && node scripts/capability-gap-test.cjs && node scripts/workflow-proposal-test.cjs && node scripts/automation-edit-proposal-test.cjs && node scripts/app-card-close-test.cjs",
"test:alert-staleness": "node scripts/alert-staleness-test.cjs",
"test:deps": "node scripts/deps-freshness-test.cjs && node scripts/runtime-account-test.cjs && node scripts/runtime-account-misattribution-test.cjs && node scripts/runtime-login-test.cjs && node scripts/claude-config-seed-test.cjs && node scripts/claude-config-isolation-test.cjs",
"test:dm-continuity": "node scripts/dm-continuity-test.cjs",
Expand Down
77 changes: 77 additions & 0 deletions scripts/git-ceiling-test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env node
/* Git discovery is fenced at the agents folder.
*
* globex 2026-09-22: an engineer run did `cd <a work dir that no longer existed>` and then, unchained,
* `git remote set-url origin …client-app` + `git reset --hard origin/<branch>`. The cd failed, git walked
* up from the agent's folder to the agent-os checkout the data home lived in, and replaced the product's
* own source with another repo — `terminal/` gone, every later session crashed on launch.
*
* Pins: every launch exports GIT_CEILING_DIRECTORIES = the agents folder; with it, git run in the agent's
* folder finds NO repo even when an ancestor is one (and without it, it would — the test proves the walk
* is real); a repo INSIDE the agent's folder still works. Isolated home; no tmux or claude needed. */
const fs = require('fs');
const os = require('os');
const path = require('path');
const { spawnSync } = require('child_process');

const ROOT = path.resolve(__dirname, '..');
const HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'aos-git-ceiling-test-'));
process.env.AGENT_OS_HOME = HOME;
process.env.AGENT_OS_TENANT = 'testco';
process.env.AOS_NO_TTYD = '1';
process.env.CLAUDE_CONFIG_DIR = path.join(HOME, 'claude-config');
fs.mkdirSync(process.env.CLAUDE_CONFIG_DIR, { recursive: true });
delete process.env.AGENT_OS_SECRET_KEY;

let pass = 0, fail = 0;
const assert = (c, name, d) => c ? (pass++, console.log(` \x1b[32m✓\x1b[0m ${name}`)) : (fail++, console.log(` \x1b[31m✗ ${name}\x1b[0m${d ? ' — ' + d : ''}`));

const { loadAgentOS } = require(path.join(ROOT, 'dist/kernel.js'));
const { TerminalManager } = require(path.join(ROOT, 'dist/terminal.js'));
const aos = loadAgentOS();
const tm = new TerminalManager(aos, 'http://127.0.0.1:0', path.join(HOME, 'tmux.sock'));

let spawned;
const live = new Set();
tm.backend.aliveNames = () => new Set(live);
tm.backend.spawn = (_s, o) => { spawned = o; live.add(o.tmuxName); };
tm.backend.kill = (_s, t) => { live.delete(t); };
tm.backend.capturePane = () => '';
tm.backend.hasClient = () => false;

// Only git's own env should decide discovery in these probes.
const cleanEnv = Object.fromEntries(Object.entries(process.env).filter(([k]) => !k.startsWith('GIT_')));
const git = (cwd, extra) => spawnSync('git', ['rev-parse', '--show-toplevel'], { cwd, env: { ...cleanEnv, ...extra }, encoding: 'utf8' });

(async () => {
const agent = aos.listAgents ? (aos.listAgents()[0]?.id) : undefined;
const AGENT = agent || 'agent-author';
tm.createSession(AGENT, 'probe', 'noop', 'automation:a1', true);
await new Promise((r) => setTimeout(r, 50));

const env = spawned?.env || {};
const agentDir = env.AGENT_DIR;
assert(!!agentDir, 'the launch carries AGENT_DIR', JSON.stringify(Object.keys(env)).slice(0, 200));
assert(env.GIT_CEILING_DIRECTORIES === fs.realpathSync(path.dirname(agentDir)),
'GIT_CEILING_DIRECTORIES is the (real) agents folder', `${env.GIT_CEILING_DIRECTORIES} vs ${agentDir}`);

// Model the live layout: the data home inside a product checkout. Make the home's PARENT chain a repo.
const product = fs.realpathSync(HOME);
spawnSync('git', ['init', '-q', product]);
const without = git(agentDir, {});
assert(without.status === 0 && without.stdout.trim() === product,
'control: WITHOUT the fence, git in the agent folder resolves to the enclosing checkout', without.stderr);
const withFence = git(agentDir, { GIT_CEILING_DIRECTORIES: env.GIT_CEILING_DIRECTORIES });
assert(withFence.status !== 0 && /not a git repository/.test(withFence.stderr),
'WITH the fence, git in the agent folder finds no repo — a stray reset cannot reach the product', withFence.stdout);

const inner = path.join(agentDir, 'repos', 'client-app');
fs.mkdirSync(inner, { recursive: true });
spawnSync('git', ['init', '-q', inner]);
const own = git(inner, { GIT_CEILING_DIRECTORIES: env.GIT_CEILING_DIRECTORIES });
assert(own.status === 0 && own.stdout.trim() === fs.realpathSync(inner), 'a repo inside the agent folder still works', own.stderr);

console.log(`\ngit-ceiling-test: ${pass} passed, ${fail} failed`);
try { fs.rmSync(HOME, { recursive: true, force: true }); } catch { /* best-effort */ }
process.exit(fail ? 1 : 0);
})();
8 changes: 8 additions & 0 deletions src/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3066,6 +3066,14 @@ export class TerminalManager {
// thread follow-ups are delivered by send-keys (see deliverToResident / reviveResident).
if (o.resident) env.RESIDENT = '1';
env.AGENT_DIR = manifest.dir;
// Fence git discovery at the agents folder. An agent's folder usually isn't a repo, so git walks UP —
// and where the data home sits inside the product checkout it lands in agent-os itself. globex
// 2026-09-22: an engineer run did `cd <a work dir that no longer existed>` then, unchained,
// `git remote set-url origin …client-app` + `git reset --hard origin/<branch>`. The cd failed, git
// resolved to the agent-os checkout, and the product's own source was replaced by another repo:
// `terminal/` gone, so every session launched afterwards crashed. Repos INSIDE the agent's folder
// (clones, worktrees, its own `git init`) are below the ceiling and unaffected.
try { env.GIT_CEILING_DIRECTORIES = fs.realpathSync(path.dirname(manifest.dir)); } catch { /* no dir → no walk to fence */ }
env.HOOK = this.gateHookFor(runtime);
// Tells the shared gate hook which tool→capability routing table to use.
env.AOS_RUNTIME = runtime;
Expand Down
Loading