@inneranimalmedia/agentsam-sdk 2.6.7 → 2.6.8
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/README.md +37 -0
- package/docs/REPO_FILE_MAP.txt +132 -0
- package/migrations/d1/0018_goap_control_plane_remaster.sql +40 -0
- package/migrations/d1/0019_agentsam_agent_run_plan_todo.sql +14 -0
- package/migrations/d1/README_PORTABLE_CONTROL_PLANE.md +20 -9
- package/migrations/runtime/0002_goap_blackboard_v1.sql +15 -0
- package/migrations/runtime/0004_agent_run_plan_linkage.sql +8 -0
- package/native/agentsam-machine/cli/Cargo.toml +8 -1
- package/native/agentsam-machine/cli/README.md +21 -0
- package/native/agentsam-machine/core/Cargo.toml +7 -0
- package/native/agentsam-machine/core/README.md +14 -0
- package/native/agentsam-machine/core/build.rs +33 -0
- package/native/agentsam-machine/core/contracts/source-types.v1.json +353 -0
- package/native/agentsam-machine/core/src/lib.rs +6 -0
- package/native/agentsam-machine/core/src/source_types.rs +2 -2
- package/package.json +16 -6
- package/packages/agentsam-errors/package.json +1 -1
- package/packages/agentsam-errors/src/index.js +1 -0
- package/packages/agentsam-errors/src/tool.js +56 -0
- package/packages/agentsam-errors/test/tool.test.mjs +58 -0
- package/packages/agentsam-goap/README.md +52 -0
- package/packages/agentsam-goap/package.json +37 -0
- package/packages/agentsam-goap/src/adapters/d1-sqlite.js +570 -0
- package/packages/agentsam-goap/src/adapters/memory.js +199 -0
- package/packages/agentsam-goap/src/contracts.js +226 -0
- package/packages/agentsam-goap/src/control-plane.js +112 -0
- package/packages/agentsam-goap/src/errors.js +157 -0
- package/packages/agentsam-goap/src/index.js +5 -0
- package/packages/agentsam-goap/src/world.js +202 -0
- package/packages/agentsam-goap/test/goap-control-plane.test.mjs +245 -0
- package/packages/agentsam-goap/test/world.test.mjs +79 -0
- package/packages/agentsam-nav/dist/Nav.d.ts +43 -0
- package/packages/agentsam-nav/dist/NavCollapsible.d.ts +10 -0
- package/packages/agentsam-nav/dist/NavContent.d.ts +2 -0
- package/packages/agentsam-nav/dist/NavFooter.d.ts +2 -0
- package/packages/agentsam-nav/dist/NavGroup.d.ts +3 -0
- package/packages/agentsam-nav/dist/NavHeader.d.ts +2 -0
- package/packages/agentsam-nav/dist/NavMenu.d.ts +22 -0
- package/packages/agentsam-nav/dist/NavProvider.d.ts +35 -0
- package/packages/agentsam-nav/dist/NavTrigger.d.ts +2 -0
- package/packages/agentsam-nav/dist/Surfaces.d.ts +41 -0
- package/packages/agentsam-nav/dist/browser.d.ts +11 -0
- package/packages/agentsam-nav/dist/browser.global.js +135 -0
- package/packages/agentsam-nav/dist/browser.js +7290 -0
- package/packages/agentsam-nav/dist/contracts.d.ts +76 -0
- package/packages/agentsam-nav/dist/index.d.ts +11 -0
- package/packages/agentsam-nav/dist/index.js +7291 -0
- package/packages/agentsam-nav/dist/legacy.css +83 -0
- package/packages/agentsam-nav/dist/shell.css +113 -0
- package/packages/agentsam-nav/dist/theme.css +113 -0
- package/packages/agentsam-nav/dist/utils.d.ts +1 -0
- package/packages/agentsam-repository/src/goap.js +13 -14
- package/packages/agentsam-repository/src/goap.js.bak-20260922011251 +670 -0
- package/packages/agentsam-repository/src/tickets.js +15 -22
- package/packages/agentsam-repository/test/goap-tickets-workspace-state.test.mjs +19 -8
- package/packages/connectors/cloudflare/package.json +3 -1
- package/packages/identity/tests/lane1-packed-consumer.test.mjs +1 -1
- package/python/agentsam_sdk/repository/intelligence/discovery.py +27 -2
- package/python/agentsam_sdk/repository/intelligence/git.py +4 -0
- package/python/tests/test_repository_intelligence.py +30 -0
- package/src/cli/command-catalog.js +2 -1
- package/src/cli.js +10 -1
- package/src/commands/goap-world.js +134 -0
- package/src/commands/goap.js +11 -4
- package/src/commands/machine-binary.js +33 -27
- package/src/commands/machine-runtime.js +266 -0
- package/src/commands/machine.js +122 -51
- package/src/commands/package.js +530 -0
- package/src/commands/plan-ledger.js +370 -0
- package/src/commands/plan.js +183 -0
- package/src/commands/skill.js +78 -17
- package/src/knowledge/source.js +1 -1
- package/src/lib/slash-commands.js +1 -1
- package/src/local/runtime-store.js +24 -2
- package/src/local/sqlite.js +17 -0
- package/src/sam/operations/package-audit.js +41 -0
- package/src/sam/seed.js +2 -0
- package/skills/grok-auth/SKILL.md +0 -110
- package/skills/grok-auth/agentsam.skill.json +0 -25
- package/skills/grok-auth/references/grok-identity.md +0 -64
- package/skills/grok-auth/references/per-user-data.md +0 -49
- package/skills/grok-auth/references/prewired-and-env.md +0 -54
- package/skills/grok-auth/references/session-ui.md +0 -133
- package/skills/grok-auth/references/sign-in-methods.md +0 -55
- package/skills/grok-auth/references/wiring.md +0 -71
- package/skills/grok-building-games/SKILL.md +0 -213
- package/skills/grok-building-games/agentsam.skill.json +0 -25
- package/skills/grok-building-games/references/3d-libs.md +0 -64
- package/skills/grok-building-games/references/ai-pathfinding.md +0 -122
- package/skills/grok-building-games/references/audio.md +0 -129
- package/skills/grok-building-games/references/babylon.md +0 -202
- package/skills/grok-building-games/references/collision-physics.md +0 -122
- package/skills/grok-building-games/references/ecs-architecture.md +0 -111
- package/skills/grok-building-games/references/game-feel-juice.md +0 -110
- package/skills/grok-building-games/references/genres/board-card-chess.md +0 -89
- package/skills/grok-building-games/references/genres/endless-runner.md +0 -83
- package/skills/grok-building-games/references/genres/fps.md +0 -105
- package/skills/grok-building-games/references/genres/platformer-2d.md +0 -97
- package/skills/grok-building-games/references/genres/puzzle-match3-tetris.md +0 -131
- package/skills/grok-building-games/references/genres/racing-kart.md +0 -106
- package/skills/grok-building-games/references/genres/topdown-twin-stick.md +0 -97
- package/skills/grok-building-games/references/genres/tower-defense.md +0 -84
- package/skills/grok-building-games/references/genres/voxel-minecraft.md +0 -89
- package/skills/grok-building-games/references/input.md +0 -139
- package/skills/grok-building-games/references/phaser.md +0 -277
- package/skills/grok-building-games/references/procedural-generation.md +0 -141
- package/skills/grok-building-games/references/save-persistence.md +0 -108
- package/skills/grok-building-games/references/threejs-foundational.md +0 -148
- package/skills/grok-controls/SKILL.md +0 -274
- package/skills/grok-controls/agentsam.skill.json +0 -25
- package/skills/grok-design-ui/SKILL.md +0 -122
- package/skills/grok-design-ui/agentsam.skill.json +0 -25
- package/skills/grok-design-ui/references/animations.md +0 -379
- package/skills/grok-design-ui/references/performance.md +0 -88
- package/skills/grok-design-ui/references/refined-ui.md +0 -384
- package/skills/grok-design-ui/references/surfaces.md +0 -247
- package/skills/grok-design-ui/references/typography.md +0 -123
- package/skills/grok-game-animation-frames/SKILL.md +0 -84
- package/skills/grok-game-animation-frames/agentsam.skill.json +0 -25
- package/skills/grok-game-asset-core/SKILL.md +0 -69
- package/skills/grok-game-asset-core/agentsam.skill.json +0 -25
- package/skills/grok-game-character-consistency/SKILL.md +0 -69
- package/skills/grok-game-character-consistency/agentsam.skill.json +0 -25
- package/skills/grok-game-tilesets/SKILL.md +0 -65
- package/skills/grok-game-tilesets/agentsam.skill.json +0 -25
- package/skills/grok-game-ui-icons/SKILL.md +0 -56
- package/skills/grok-game-ui-icons/agentsam.skill.json +0 -25
- package/skills/grok-generate2dmap/LICENSE +0 -21
- package/skills/grok-generate2dmap/SKILL.md +0 -120
- package/skills/grok-generate2dmap/SOURCE.md +0 -6
- package/skills/grok-generate2dmap/agentsam.skill.json +0 -25
- package/skills/grok-generate2dmap/references/deliverables.md +0 -101
- package/skills/grok-generate2dmap/references/layered-map-contract.md +0 -209
- package/skills/grok-generate2dmap/references/map-strategies.md +0 -341
- package/skills/grok-generate2dmap/references/object-production-gate.md +0 -24
- package/skills/grok-generate2dmap/references/pipeline.md +0 -149
- package/skills/grok-generate2dmap/references/prop-pack-contract.md +0 -174
- package/skills/grok-generate2dmap/references/side-scroll-stages.md +0 -29
- package/skills/grok-generate2dmap/scripts/compose_layered_preview.py +0 -157
- package/skills/grok-generate2dmap/scripts/extract_prop_pack.py +0 -369
- package/skills/grok-generate2dsprite/LICENSE +0 -21
- package/skills/grok-generate2dsprite/SKILL.md +0 -313
- package/skills/grok-generate2dsprite/SOURCE.md +0 -6
- package/skills/grok-generate2dsprite/agentsam.skill.json +0 -25
- package/skills/grok-generate2dsprite/references/modes.md +0 -124
- package/skills/grok-generate2dsprite/references/prompt-rules.md +0 -315
- package/skills/grok-generate2dsprite/scripts/generate2dsprite.py +0 -1031
- package/skills/grok-generate2dsprite/scripts/make_layout_guide.py +0 -109
- package/skills/grok-imagine/SKILL.md +0 -201
- package/skills/grok-imagine/agentsam.skill.json +0 -25
- package/skills/grok-multiplayer-p2p/SKILL.md +0 -111
- package/skills/grok-multiplayer-p2p/agentsam.skill.json +0 -25
- package/skills/grok-multiplayer-p2p/references/react-binding.md +0 -144
- package/skills/grok-multiplayer-p2p/references/signaling-relay.md +0 -269
- package/skills/grok-neon/SKILL.md +0 -157
- package/skills/grok-neon/agentsam.skill.json +0 -25
- package/skills/grok-og/SKILL.md +0 -102
- package/skills/grok-og/agentsam.skill.json +0 -25
- package/skills/grok-og/references/brand-pass.md +0 -53
- package/skills/grok-og/references/custom-card.md +0 -107
- package/skills/grok-og/references/favicon-and-icons.md +0 -48
- package/skills/grok-og/references/og-type-contract.md +0 -18
- package/skills/grok-og/references/placeholder-card.md +0 -21
- package/skills/grok-og/references/x-banner.md +0 -41
- package/skills/grok-threejs/SKILL.md +0 -67
- package/skills/grok-threejs/agentsam.skill.json +0 -25
- package/skills/grok-threejs/references/llms-full.txt +0 -2780
- package/skills/grok-video2dsprite/LICENSE +0 -21
- package/skills/grok-video2dsprite/SKILL.md +0 -236
- package/skills/grok-video2dsprite/SOURCE.md +0 -6
- package/skills/grok-video2dsprite/agentsam.skill.json +0 -25
- package/skills/grok-video2dsprite/references/pipeline.md +0 -90
- package/skills/grok-video2dsprite/references/prompt-rules.md +0 -63
- package/skills/grok-video2dsprite/scripts/video2dsprite.py +0 -490
- package/skills/grok-xai-api/SKILL.md +0 -152
- package/skills/grok-xai-api/agentsam.skill.json +0 -25
- package/test/account-session.test.mjs +0 -101
- package/test/agent-capabilities.test.mjs +0 -67
- package/test/app-authority-context.test.mjs +0 -32
- package/test/app-building-skills.test.mjs +0 -61
- package/test/apps-scaffold-contract.test.mjs +0 -115
- package/test/blender-cad.test.mjs +0 -146
- package/test/cad-docker.test.mjs +0 -96
- package/test/capabilities.test.mjs +0 -133
- package/test/cli/api-key-env-whoami.test.mjs +0 -129
- package/test/cli/catalog-authority.test.mjs +0 -64
- package/test/cli/codebaseindex-plan-ux.test.mjs +0 -37
- package/test/cli/eval-live.test.mjs +0 -219
- package/test/cli/go.test.mjs +0 -128
- package/test/cli/local-vault-and-provider-setup.test.mjs +0 -268
- package/test/cli/machine-command.test.mjs +0 -34
- package/test/cli/mcp-client.test.mjs +0 -256
- package/test/cli/preferences-runtime.test.mjs +0 -11
- package/test/cli/runtime-persist.test.mjs +0 -56
- package/test/cli/runtime-ui.test.mjs +0 -141
- package/test/cli/rust-command.test.mjs +0 -21
- package/test/cli/skill-npm-and-env.test.mjs +0 -52
- package/test/cli/status-actions.test.mjs +0 -88
- package/test/cli/wireframes-go-registry.test.mjs +0 -139
- package/test/cli-preferences.test.mjs +0 -46
- package/test/cloudflare-connector.test.mjs +0 -93
- package/test/cloudflare-runtime.test.mjs +0 -82
- package/test/context.test.mjs +0 -159
- package/test/credentials-audit.test.mjs +0 -113
- package/test/deploy-health-scan.test.mjs +0 -95
- package/test/deploy-receipt.test.mjs +0 -91
- package/test/dockerize.test.mjs +0 -93
- package/test/error-diagnostics.test.mjs +0 -124
- package/test/eval-context.test.mjs +0 -37
- package/test/execution-approvals.test.mjs +0 -27
- package/test/fixtures/cad/courtyard-house.json +0 -208
- package/test/fixtures/knowledge-rpc-worker.mjs +0 -16
- package/test/fixtures/project-authority/customer-app/.agentsam/app.json +0 -5
- package/test/fixtures/project-authority/customer-app/apps/my-product/index.ts +0 -1
- package/test/fixtures/project-authority/customer-app/wrangler.toml +0 -17
- package/test/gcloud-auth.test.mjs +0 -111
- package/test/go/build-source-identity.test.mjs +0 -31
- package/test/go/cloudflare-probe.test.mjs +0 -317
- package/test/go/distribution.test.mjs +0 -28
- package/test/google-cloud-connection.test.mjs +0 -83
- package/test/google-cloud-inventory.test.mjs +0 -64
- package/test/indexing-provider.test.mjs +0 -29
- package/test/init-options.test.mjs +0 -35
- package/test/integration/ai-access-onboarding.test.mjs +0 -47
- package/test/integration/app-command.test.mjs +0 -85
- package/test/integration/autorag-project-authority.integration.test.mjs +0 -51
- package/test/integration/autorag-runtime-discovery.test.mjs +0 -107
- package/test/integration/cad-discovery.test.mjs +0 -158
- package/test/integration/cad-docker-executor.test.mjs +0 -249
- package/test/integration/cad-execution.test.mjs +0 -184
- package/test/integration/cad-project-app.test.mjs +0 -31
- package/test/integration/cad-project-d1.test.mjs +0 -25
- package/test/integration/cad-project-native.test.mjs +0 -24
- package/test/integration/cad-project-workflow.test.mjs +0 -9
- package/test/integration/cad-project.test.mjs +0 -85
- package/test/integration/cli-help.test.mjs +0 -41
- package/test/integration/cms-app-scaffold.test.mjs +0 -77
- package/test/integration/cms-site-tenancy-contract.test.mjs +0 -268
- package/test/integration/codebaseindex-ingest.test.mjs +0 -255
- package/test/integration/core-client.integration.test.mjs +0 -55
- package/test/integration/database-hosted-honesty.test.mjs +0 -49
- package/test/integration/desktop-keychain-boundary.test.mjs +0 -45
- package/test/integration/error-runtime-rpc.test.mjs +0 -71
- package/test/integration/features-resolve.test.mjs +0 -147
- package/test/integration/hosted-work-host-authority.test.mjs +0 -47
- package/test/integration/icon-registry.test.mjs +0 -67
- package/test/integration/ingest-discover-models.test.mjs +0 -30
- package/test/integration/inspect-path-cli.test.mjs +0 -46
- package/test/integration/install-script.test.mjs +0 -110
- package/test/integration/inventory-core.test.mjs +0 -53
- package/test/integration/inventory-eligibility.test.mjs +0 -59
- package/test/integration/inventory-evidence.test.mjs +0 -125
- package/test/integration/knowledge-rpc.test.mjs +0 -112
- package/test/integration/local-fs.test.mjs +0 -113
- package/test/integration/local-provider-bridge.test.mjs +0 -65
- package/test/integration/local-studio-settings-production.test.mjs +0 -40
- package/test/integration/lockfile-sync.test.mjs +0 -26
- package/test/integration/merkle-cli.test.mjs +0 -61
- package/test/integration/merkle-persistence-identity.test.mjs +0 -48
- package/test/integration/plugins-cloudflare.integration.test.mjs +0 -28
- package/test/integration/provider-env-cli.test.mjs +0 -60
- package/test/integration/provider-factory.test.mjs +0 -197
- package/test/integration/repository-company-graph.test.mjs +0 -90
- package/test/integration/runtime-migrations.test.mjs +0 -87
- package/test/integration/runtime-status.integration.test.mjs +0 -227
- package/test/integration/sam-activity-recovery.test.mjs +0 -147
- package/test/integration/sam-decision.test.mjs +0 -584
- package/test/integration/sam-kernel.test.mjs +0 -99
- package/test/integration/sam-planning-astar.test.mjs +0 -279
- package/test/integration/scaffold-cloudflare.test.mjs +0 -46
- package/test/integration/skill-runtime.test.mjs +0 -240
- package/test/integration/storage-architecture.test.mjs +0 -154
- package/test/integration/store-discovery.test.mjs +0 -44
- package/test/integration/studio-fs-pty-e2e.test.mjs +0 -294
- package/test/integration/terminal-exec.integration.test.mjs +0 -86
- package/test/integration/theme-package-registry.test.mjs +0 -31
- package/test/integration/tool-runtime.test.mjs +0 -124
- package/test/integration/tool-schema.test.mjs +0 -59
- package/test/jr-dev-skill.test.mjs +0 -26
- package/test/knowledge-context-pack.test.mjs +0 -19
- package/test/knowledge-postgres.test.mjs +0 -40
- package/test/knowledge-service.test.mjs +0 -110
- package/test/knowledge.test.mjs +0 -206
- package/test/live/terminal-transport.live.test.mjs +0 -24
- package/test/local-sessions.test.mjs +0 -57
- package/test/local-studio-deploy.test.mjs +0 -83
- package/test/loopback-oauth.test.mjs +0 -53
- package/test/merkle-render.test.mjs +0 -31
- package/test/mini.test.mjs +0 -140
- package/test/model-catalog.test.mjs +0 -70
- package/test/models.test.mjs +0 -309
- package/test/npm10-lock.test.mjs +0 -29
- package/test/ollama.test.mjs +0 -137
- package/test/open-url.test.mjs +0 -64
- package/test/openai-responses.test.mjs +0 -155
- package/test/portable-context.test.mjs +0 -94
- package/test/project-config.test.mjs +0 -81
- package/test/project-rules.test.mjs +0 -44
- package/test/provider-credentials.test.mjs +0 -106
- package/test/rehydrate.test.mjs +0 -25
- package/test/release-hygiene.test.mjs +0 -42
- package/test/repository-snapshot-view.test.mjs +0 -112
- package/test/responses-runner.test.mjs +0 -465
- package/test/runtime-activity.test.mjs +0 -98
- package/test/sdk-worker-contract.test.mjs +0 -72
- package/test/security.test.mjs +0 -266
- package/test/setup-planner.test.mjs +0 -74
- package/test/setup-runtime.test.mjs +0 -51
- package/test/shell.test.mjs +0 -281
- package/test/skills.test.mjs +0 -22
- package/test/smoke.mjs +0 -122
- package/test/telemetry.test.mjs +0 -79
- package/test/terminal/local-pty.mock.test.mjs +0 -153
- package/test/terminal-machine-identity.test.mjs +0 -88
- package/test/theme-portability.test.mjs +0 -14
- package/test/tools-search.test.mjs +0 -73
- package/test/unit/website-assets-bindings.test.mjs +0 -60
- package/test/whoami-resume.test.mjs +0 -157
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { spawnSync } from 'node:child_process';
|
|
6
|
-
import test from 'node:test';
|
|
7
|
-
import { dispatchShellLine } from '../../src/commands/shell.js';
|
|
8
|
-
import { createLocalSession, listLocalSessions, loadLocalSession, saveLocalSession } from '../../src/lib/local-sessions.js';
|
|
9
|
-
import { createLocalSqliteDatabaseSync } from '../../src/local/sqlite.js';
|
|
10
|
-
import { applyRuntimeMigrationsSync } from '../../src/local/migrations.js';
|
|
11
|
-
import { finishRuntimeRun, pruneExpiredRuntimeState, runtimeDatabasePath, startRuntimeRun } from '../../src/local/runtime-store.js';
|
|
12
|
-
|
|
13
|
-
const repoRoot = path.resolve(new URL('../..', import.meta.url).pathname);
|
|
14
|
-
|
|
15
|
-
function fixture(t) {
|
|
16
|
-
const base = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-storage-'));
|
|
17
|
-
t.after(() => fs.rmSync(base, { recursive: true, force: true }));
|
|
18
|
-
const home = path.join(base, 'home');
|
|
19
|
-
const projectA = path.join(base, 'project-a');
|
|
20
|
-
const projectB = path.join(base, 'project-b');
|
|
21
|
-
for (const root of [projectA, projectB]) {
|
|
22
|
-
fs.mkdirSync(path.join(root, '.agentsam'), { recursive: true });
|
|
23
|
-
fs.writeFileSync(path.join(root, '.agentsam', 'config.json'), '{"schema_version":2}\n');
|
|
24
|
-
}
|
|
25
|
-
fs.mkdirSync(home);
|
|
26
|
-
return { base, home, projectA, projectB };
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
test('S1: a local shell boots and migrates SQLite without cloud configuration', (t) => {
|
|
30
|
-
const { home, projectA } = fixture(t);
|
|
31
|
-
const env = { ...process.env, HOME: home };
|
|
32
|
-
for (const key of ['CLOUDFLARE_API_TOKEN', 'AGENTSAM_API_KEY', 'SUPABASE_URL', 'DATABASE_URL']) delete env[key];
|
|
33
|
-
const result = spawnSync(process.execPath, [path.join(repoRoot, 'src/cli.js'), 'shell'], {
|
|
34
|
-
cwd: projectA, env, input: '/exit\n', encoding: 'utf8', timeout: 10_000,
|
|
35
|
-
});
|
|
36
|
-
assert.equal(result.status, 0, result.stderr);
|
|
37
|
-
assert.match(result.stdout, /Interactive shell ready/);
|
|
38
|
-
assert.equal(fs.existsSync(runtimeDatabasePath(projectA)), true);
|
|
39
|
-
assert.equal(fs.existsSync(path.join(home, '.agentsam', 'sessions')), false);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test('S2-S4: cwd and model changes preserve project state; repository switch selects a new authority', async (t) => {
|
|
43
|
-
const { home, projectA, projectB } = fixture(t);
|
|
44
|
-
const child = path.join(projectA, 'src', 'agent');
|
|
45
|
-
fs.mkdirSync(child, { recursive: true });
|
|
46
|
-
assert.equal(runtimeDatabasePath(child), runtimeDatabasePath(projectA));
|
|
47
|
-
|
|
48
|
-
const session = createLocalSession({ cwd: child, title: 'Project A' }, { home });
|
|
49
|
-
const changedModel = saveLocalSession({ ...session, model_key: 'gemini:example' }, { home });
|
|
50
|
-
assert.equal(changedModel.id, session.id);
|
|
51
|
-
assert.equal(changedModel.project_root, projectA);
|
|
52
|
-
assert.equal(loadLocalSession(session.id, { home, cwd: projectA }).model_key, 'gemini:example');
|
|
53
|
-
assert.equal(loadLocalSession(session.id, { home, cwd: projectB }), null);
|
|
54
|
-
|
|
55
|
-
const runId = await startRuntimeRun({ projectRoot: projectA, model_key: 'test:model' });
|
|
56
|
-
let output = '';
|
|
57
|
-
const state = { cwd: child, projectRoot: projectA, home, session: changedModel, interactive: false, write(value) { output += value; } };
|
|
58
|
-
await dispatchShellLine('/logs', state);
|
|
59
|
-
assert.match(output, new RegExp(runId));
|
|
60
|
-
await dispatchShellLine(`/cd ${projectB}`, state);
|
|
61
|
-
assert.equal(state.cwd, projectB);
|
|
62
|
-
assert.equal(state.projectRoot, projectB);
|
|
63
|
-
assert.notEqual(state.session.id, session.id);
|
|
64
|
-
assert.equal(listLocalSessions({ home, cwd: projectA })[0].id, session.id);
|
|
65
|
-
assert.equal(listLocalSessions({ home, cwd: projectB })[0].id, state.session.id);
|
|
66
|
-
assert.equal(fs.existsSync(path.join(child, '.agentsam', 'data', 'agentsam.sqlite')), false);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test('S5: expiry cleanup removes transient digests and retains completed run receipts', async (t) => {
|
|
70
|
-
const { projectA } = fixture(t);
|
|
71
|
-
const runId = await startRuntimeRun({ cwd: projectA, model_key: 'test:model' });
|
|
72
|
-
await finishRuntimeRun({ cwd: projectA, id: runId, status: 'completed' });
|
|
73
|
-
const db = createLocalSqliteDatabaseSync(runtimeDatabasePath(projectA));
|
|
74
|
-
db.prepare(`INSERT INTO agentsam_context_digest
|
|
75
|
-
(id, digest_type, agent_run_id, source_hash, digest_hash, digest_text, expires_at_unix)
|
|
76
|
-
VALUES (?, 'session', ?, 'source', 'digest', 'expired', 1)`).run('ctx_expired', runId);
|
|
77
|
-
db.prepare('INSERT INTO agentsam_compaction_events (id, agent_run_id, expires_at_epoch) VALUES (?, ?, 1)').run('cmp_expired', runId);
|
|
78
|
-
db.close();
|
|
79
|
-
|
|
80
|
-
const pruned = await pruneExpiredRuntimeState({ cwd: projectA, nowUnix: 2 });
|
|
81
|
-
assert.deepEqual(pruned, { digests: 1, compactions: 1, approvals_expired: 0 });
|
|
82
|
-
const verified = createLocalSqliteDatabaseSync(runtimeDatabasePath(projectA));
|
|
83
|
-
assert.equal(verified.prepare('SELECT status FROM agentsam_agent_run WHERE id = ?').get(runId).status, 'completed');
|
|
84
|
-
assert.equal(verified.prepare('SELECT COUNT(*) AS n FROM agentsam_context_digest').get().n, 0);
|
|
85
|
-
verified.close();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test('S6-S7: connected Worker bindings do not move sessions or store credential values', (t) => {
|
|
89
|
-
const { home, projectA } = fixture(t);
|
|
90
|
-
const workerConfig = fs.readFileSync(path.join(repoRoot, 'apps/local-studio/backend/wrangler.jsonc'), 'utf8');
|
|
91
|
-
for (const binding of ['"d1_databases"', '"hyperdrive"', '"r2_buckets"', '"services"']) assert.match(workerConfig, new RegExp(binding));
|
|
92
|
-
assert.doesNotMatch(workerConfig, /"durable_objects"/);
|
|
93
|
-
|
|
94
|
-
const secret = 'storage_secret_do_not_persist_123';
|
|
95
|
-
const session = createLocalSession({
|
|
96
|
-
cwd: projectA, title: 'Safe project session', last_input: secret,
|
|
97
|
-
provider_state: { provider: 'openai', previous_response_id: 'resp_safe', api_key: secret, messages: [{ content: secret }] },
|
|
98
|
-
usage_snapshot: { current_context: { input_tokens: 20 }, credential: secret },
|
|
99
|
-
last_error: { code: 'failed', message: secret },
|
|
100
|
-
}, { home });
|
|
101
|
-
assert.equal(session.storage.runtime, 'sqlite');
|
|
102
|
-
assert.equal(session.storage.remote, null);
|
|
103
|
-
assert.equal(session.storage.capabilities.remote_shared_state, false);
|
|
104
|
-
assert.equal(session.storage.capabilities.distributed_actor_semantics, false);
|
|
105
|
-
assert.equal(session.last_input, null);
|
|
106
|
-
assert.equal(session.provider_state.api_key, undefined);
|
|
107
|
-
assert.doesNotMatch(fs.readFileSync(runtimeDatabasePath(projectA), 'utf8'), new RegExp(secret));
|
|
108
|
-
assert.equal(fs.statSync(runtimeDatabasePath(projectA)).mode & 0o777, 0o600);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test('S8: canonical migrations upgrade an older runtime database for project sessions', (t) => {
|
|
112
|
-
const { base, home, projectA } = fixture(t);
|
|
113
|
-
const oldMigrations = path.join(base, 'old-migrations');
|
|
114
|
-
fs.mkdirSync(oldMigrations);
|
|
115
|
-
fs.copyFileSync(path.join(repoRoot, 'migrations/runtime/0001_cli_runtime.sql'), path.join(oldMigrations, '0001_cli_runtime.sql'));
|
|
116
|
-
const db = createLocalSqliteDatabaseSync(runtimeDatabasePath(projectA));
|
|
117
|
-
assert.equal(applyRuntimeMigrationsSync(db, { migrationsDir: oldMigrations }).applied, 1);
|
|
118
|
-
assert.equal(db.prepare("SELECT name FROM sqlite_master WHERE name = 'agentsam_project_sessions'").get(), undefined);
|
|
119
|
-
db.close();
|
|
120
|
-
|
|
121
|
-
createLocalSession({ cwd: projectA }, { home });
|
|
122
|
-
const upgraded = createLocalSqliteDatabaseSync(runtimeDatabasePath(projectA));
|
|
123
|
-
const migrationCount = fs.readdirSync(path.join(repoRoot, 'migrations', 'runtime'))
|
|
124
|
-
.filter((name) => /^\d+.*\.sql$/i.test(name)).length;
|
|
125
|
-
assert.equal(upgraded.prepare("SELECT COUNT(*) AS n FROM agentsam_schema_migrations").get().n, migrationCount);
|
|
126
|
-
assert.ok(upgraded.prepare("SELECT name FROM sqlite_master WHERE name = 'agentsam_project_sessions'").get());
|
|
127
|
-
upgraded.close();
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test('legacy home-level JSON sessions import only into their matching project SQLite', (t) => {
|
|
131
|
-
const { home, projectA, projectB } = fixture(t);
|
|
132
|
-
const id = 'asess_00000000-0000-4000-8000-000000000099';
|
|
133
|
-
const legacyDir = path.join(home, '.agentsam', 'sessions');
|
|
134
|
-
fs.mkdirSync(legacyDir, { recursive: true });
|
|
135
|
-
const filename = path.join(legacyDir, `${id}.json`);
|
|
136
|
-
fs.writeFileSync(filename, JSON.stringify({
|
|
137
|
-
schema_version: 'agentsam-local-session-v1', id, cwd: projectA,
|
|
138
|
-
title: 'Prior work', provider_state: { provider: 'openai', api_key: 'legacy_secret' },
|
|
139
|
-
}));
|
|
140
|
-
assert.equal(loadLocalSession(id, { home, cwd: projectB }), null);
|
|
141
|
-
assert.equal(fs.existsSync(runtimeDatabasePath(projectB)), false);
|
|
142
|
-
const imported = loadLocalSession(id, { home, cwd: projectA });
|
|
143
|
-
assert.equal(imported.project_root, projectA);
|
|
144
|
-
assert.equal(imported.provider_state.api_key, undefined);
|
|
145
|
-
assert.equal(fs.existsSync(filename), true);
|
|
146
|
-
assert.doesNotMatch(fs.readFileSync(runtimeDatabasePath(projectA), 'utf8'), /legacy_secret/);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test('S9: default runtime has no Durable Object binding or dependency', () => {
|
|
150
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
|
|
151
|
-
assert.equal(Object.keys(pkg.dependencies || {}).some((name) => /durable.?object/i.test(name)), false);
|
|
152
|
-
const shell = fs.readFileSync(path.join(repoRoot, 'src/commands/shell.js'), 'utf8');
|
|
153
|
-
assert.doesNotMatch(shell, /\bDurableObject\b|\bdurable_objects\b|\bidFromName\s*\(/);
|
|
154
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { discoverProjectStores, selectKnowledgeStore } from '../../src/knowledge/store-discovery.js';
|
|
7
|
-
import { initRepository, readConfig } from '../../src/knowledge/config.js';
|
|
8
|
-
import { dispatchShellLine } from '../../src/commands/shell.js';
|
|
9
|
-
|
|
10
|
-
test('store discovery reads JSONC bindings without leaking credentials or selecting application storage', async () => {
|
|
11
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-store-discovery-'));
|
|
12
|
-
fs.writeFileSync(path.join(root, 'package.json'), '{"name":"stores"}');
|
|
13
|
-
fs.writeFileSync(path.join(root, 'wrangler.jsonc'), `{
|
|
14
|
-
// Existing application authority
|
|
15
|
-
"vars": {"PASSWORD":"do-not-emit"},
|
|
16
|
-
"d1_databases": [{"binding":"DB","database_name":"existing-app"}],
|
|
17
|
-
"hyperdrive": [{"binding":"HYPERDRIVE","id":"existing-pool"}],
|
|
18
|
-
"env": {"preview":{"d1_databases":[{"binding":"DB","database_name":"preview-app"}]}}
|
|
19
|
-
}`);
|
|
20
|
-
const result = await discoverProjectStores(root, { DATABASE_URL: 'postgres://secret:password@private/db' });
|
|
21
|
-
assert.equal(result.candidates.find(c => c.driver === 'd1').name, 'existing-app');
|
|
22
|
-
assert.equal(result.candidates.find(c => c.environment === 'preview').name, 'preview-app');
|
|
23
|
-
assert.ok(result.candidates.filter(c => c.role === 'application').every(c => c.selectable === false));
|
|
24
|
-
assert.equal(result.candidates.find(c => c.driver === 'postgres').connection_env, 'DATABASE_URL');
|
|
25
|
-
assert.doesNotMatch(JSON.stringify(result), /password|secret|do-not-emit/);
|
|
26
|
-
assert.equal(fs.existsSync(path.join(root, '.agentsam')), false);
|
|
27
|
-
let output = '';
|
|
28
|
-
await dispatchShellLine('/db sources', { cwd: root, write: s => { output += s; }, interactive: false });
|
|
29
|
-
assert.match(output, /existing-app/);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('knowledge selection edits existing portable config without overwriting scope or moving runtime data', () => {
|
|
33
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-store-selection-'));
|
|
34
|
-
fs.writeFileSync(path.join(root, 'package.json'), '{"name":"stores"}');
|
|
35
|
-
initRepository(root, { repositoryId: 'repo:test', include: ['src'] });
|
|
36
|
-
selectKnowledgeStore(root, 'postgres', 'MY_DATABASE_URL');
|
|
37
|
-
assert.deepEqual(readConfig(root).scope.include, ['src']);
|
|
38
|
-
assert.equal(readConfig(root).storage.connection_env, 'MY_DATABASE_URL');
|
|
39
|
-
assert.equal(fs.existsSync(path.join(root, '.agentsam/data/agentsam.sqlite')), false);
|
|
40
|
-
assert.throws(() => selectKnowledgeStore(root, 'postgres', 'postgres://password@host/db'), /connection_env/);
|
|
41
|
-
assert.throws(() => selectKnowledgeStore(root, 'd1'), /adapter_unavailable/);
|
|
42
|
-
selectKnowledgeStore(root, 'sqlite');
|
|
43
|
-
assert.deepEqual(readConfig(root).storage, { driver: 'sqlite' });
|
|
44
|
-
});
|
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Product-path filesystem ↔ PTY proof.
|
|
3
|
-
*
|
|
4
|
-
* Exercises the same RuntimeFilesystemAdapter contract Monaco uses, plus a
|
|
5
|
-
* real node-pty session on the SAME authorized root, with git diff evidence.
|
|
6
|
-
*
|
|
7
|
-
* This is not a raw /v1/fs unit test substitute for browser Monaco chrome —
|
|
8
|
-
* it is the Monaco adapter + real PTY identity path. Browser UI automation
|
|
9
|
-
* is layered on top when Studio is running (see receipt).
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import assert from 'node:assert/strict';
|
|
13
|
-
import { spawnSync } from 'node:child_process';
|
|
14
|
-
import fs from 'node:fs';
|
|
15
|
-
import os from 'node:os';
|
|
16
|
-
import path from 'node:path';
|
|
17
|
-
import { after, before, describe, it } from 'node:test';
|
|
18
|
-
import { WebSocket } from 'ws';
|
|
19
|
-
import { startLocalPtyServer } from '../../src/local-pty/server.js';
|
|
20
|
-
import { getRepositoryFreshness } from '../../src/local-fs/freshness.js';
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Minimal JS twin of RuntimeFilesystemAdapter (Studio Monaco path).
|
|
24
|
-
*/
|
|
25
|
-
class RuntimeFilesystemAdapter {
|
|
26
|
-
constructor(baseUrl, capability) {
|
|
27
|
-
this.baseUrl = String(baseUrl).replace(/\/$/, '');
|
|
28
|
-
this.capability = capability;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
headers(json = false) {
|
|
32
|
-
const h = {};
|
|
33
|
-
if (json) h['Content-Type'] = 'application/json';
|
|
34
|
-
if (this.capability) {
|
|
35
|
-
h.Authorization = `Bearer ${this.capability}`;
|
|
36
|
-
h['x-agentsam-workspace-capability'] = this.capability;
|
|
37
|
-
}
|
|
38
|
-
return h;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async read(filePath) {
|
|
42
|
-
const u = new URL(`${this.baseUrl}/v1/fs/read`);
|
|
43
|
-
u.searchParams.set('path', filePath);
|
|
44
|
-
const res = await fetch(u, { headers: this.headers() });
|
|
45
|
-
return res.json();
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async write(filePath, content, expectedVersion, overwrite = false) {
|
|
49
|
-
const res = await fetch(`${this.baseUrl}/v1/fs/write`, {
|
|
50
|
-
method: 'POST',
|
|
51
|
-
headers: this.headers(true),
|
|
52
|
-
body: JSON.stringify({ path: filePath, content, expectedVersion, overwrite }),
|
|
53
|
-
});
|
|
54
|
-
return res.json();
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function runGit(cwd, args) {
|
|
59
|
-
return spawnSync('git', args, { cwd, encoding: 'utf8' });
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function wait(ms) {
|
|
63
|
-
return new Promise((r) => setTimeout(r, ms));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Drive a real PTY over the local WS wire: send a command, collect output.
|
|
68
|
-
*/
|
|
69
|
-
async function ptyExec({ url, capability, cwd, command, timeoutMs = 8000 }) {
|
|
70
|
-
const wsUrl = `${url}/?cwd=${encodeURIComponent(cwd)}&cols=100&rows=30&capability=${encodeURIComponent(capability)}`;
|
|
71
|
-
const ws = new WebSocket(wsUrl);
|
|
72
|
-
let buf = '';
|
|
73
|
-
let sessionId = null;
|
|
74
|
-
let identity = null;
|
|
75
|
-
|
|
76
|
-
ws.on('message', (data) => {
|
|
77
|
-
const text = Buffer.isBuffer(data) ? data.toString('utf8') : String(data);
|
|
78
|
-
if (text.startsWith('{')) {
|
|
79
|
-
try {
|
|
80
|
-
const msg = JSON.parse(text);
|
|
81
|
-
if (msg.type === 'session_id') sessionId = msg.session_id;
|
|
82
|
-
if (msg.type === 'workspace_identity') {
|
|
83
|
-
identity = msg;
|
|
84
|
-
sessionId = msg.session_id || sessionId;
|
|
85
|
-
}
|
|
86
|
-
if (msg.type === 'error') buf += `\nPTY_ERROR:${msg.code}:${msg.message}\n`;
|
|
87
|
-
return;
|
|
88
|
-
} catch {
|
|
89
|
-
/* fall through */
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
buf += text;
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
await new Promise((resolve, reject) => {
|
|
96
|
-
const t = setTimeout(() => reject(new Error('pty_connect_timeout')), 5000);
|
|
97
|
-
ws.on('open', () => {
|
|
98
|
-
clearTimeout(t);
|
|
99
|
-
resolve();
|
|
100
|
-
});
|
|
101
|
-
ws.on('error', (err) => {
|
|
102
|
-
clearTimeout(t);
|
|
103
|
-
reject(err);
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
// Wait for bash prompt
|
|
108
|
-
const promptDeadline = Date.now() + 3000;
|
|
109
|
-
while (Date.now() < promptDeadline) {
|
|
110
|
-
if (/[$#]\s*$/.test(buf.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, '').trimEnd()) || buf.includes('bash')) break;
|
|
111
|
-
await wait(50);
|
|
112
|
-
}
|
|
113
|
-
await wait(100);
|
|
114
|
-
ws.send(`${command}\r`);
|
|
115
|
-
const deadline = Date.now() + timeoutMs;
|
|
116
|
-
while (Date.now() < deadline) {
|
|
117
|
-
await wait(150);
|
|
118
|
-
const plain = buf.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, '');
|
|
119
|
-
if (plain.includes('MONACO_MARK_DONE') || plain.includes('diff --git') || plain.includes('+##')) {
|
|
120
|
-
break;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
await wait(400);
|
|
124
|
-
ws.close();
|
|
125
|
-
return { output: buf, sessionId, identity };
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
describe('Studio filesystem ↔ real PTY product proof', () => {
|
|
129
|
-
/** @type {string} */
|
|
130
|
-
let root;
|
|
131
|
-
/** @type {Awaited<ReturnType<typeof startLocalPtyServer>>} */
|
|
132
|
-
let server;
|
|
133
|
-
/** @type {RuntimeFilesystemAdapter} */
|
|
134
|
-
let adapter;
|
|
135
|
-
const monacoMarker = `MONACO_MARK_${Date.now()}`;
|
|
136
|
-
const ptyMarker = `PTY_MARK_${Date.now()}`;
|
|
137
|
-
|
|
138
|
-
before(async () => {
|
|
139
|
-
root = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-fs-e2e-'));
|
|
140
|
-
runGit(root, ['init']);
|
|
141
|
-
runGit(root, ['config', 'user.email', 'agentsam-e2e@local']);
|
|
142
|
-
runGit(root, ['config', 'user.name', 'AgentSam E2E']);
|
|
143
|
-
fs.writeFileSync(path.join(root, 'README.md'), '# disposable\n');
|
|
144
|
-
fs.writeFileSync(path.join(root, 'NOTES.md'), 'stable\n');
|
|
145
|
-
runGit(root, ['add', '.']);
|
|
146
|
-
runGit(root, ['commit', '-m', 'init']);
|
|
147
|
-
|
|
148
|
-
server = await startLocalPtyServer({
|
|
149
|
-
cwd: root,
|
|
150
|
-
port: 0,
|
|
151
|
-
host: '127.0.0.1',
|
|
152
|
-
shell: '/bin/bash',
|
|
153
|
-
});
|
|
154
|
-
assert.ok(server.capability?.capability);
|
|
155
|
-
assert.equal(server.cwd, path.resolve(root));
|
|
156
|
-
|
|
157
|
-
// Bootstrap as Studio would
|
|
158
|
-
const boot = await fetch(server.bootstrapUrl);
|
|
159
|
-
const claim = await boot.json();
|
|
160
|
-
assert.equal(claim.ok, true);
|
|
161
|
-
assert.equal(claim.root, path.resolve(root));
|
|
162
|
-
assert.equal(claim.capability, server.capability.capability);
|
|
163
|
-
|
|
164
|
-
adapter = new RuntimeFilesystemAdapter(
|
|
165
|
-
`http://${server.host}:${server.port}`,
|
|
166
|
-
claim.capability,
|
|
167
|
-
);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
after(async () => {
|
|
171
|
-
if (server) await server.close().catch(() => {});
|
|
172
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it('rejects FS without capability', async () => {
|
|
176
|
-
const res = await fetch(`http://${server.host}:${server.port}/v1/fs/read?path=README.md`);
|
|
177
|
-
const body = await res.json();
|
|
178
|
-
assert.equal(body.ok, false);
|
|
179
|
-
assert.equal(body.code, 'WORKSPACE_CAPABILITY_REQUIRED');
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('rejects arbitrary root claim', async () => {
|
|
183
|
-
const outside = os.tmpdir();
|
|
184
|
-
const res = await fetch(
|
|
185
|
-
`${server.bootstrapUrl}?root=${encodeURIComponent(outside)}`,
|
|
186
|
-
);
|
|
187
|
-
const body = await res.json();
|
|
188
|
-
assert.equal(body.ok, false);
|
|
189
|
-
assert.equal(body.code, 'WORKSPACE_ROOT_MISMATCH');
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('Monaco adapter write → real PTY git diff sees the edit', async () => {
|
|
193
|
-
const before = await adapter.read('README.md');
|
|
194
|
-
assert.equal(before.ok, true);
|
|
195
|
-
const next = `${before.content}\n## ${monacoMarker}\n`;
|
|
196
|
-
const wrote = await adapter.write('README.md', next, before.version);
|
|
197
|
-
assert.equal(wrote.ok, true, JSON.stringify(wrote));
|
|
198
|
-
|
|
199
|
-
// Disk truth
|
|
200
|
-
const disk = fs.readFileSync(path.join(root, 'README.md'), 'utf8');
|
|
201
|
-
assert.ok(disk.includes(monacoMarker));
|
|
202
|
-
|
|
203
|
-
// Same-root PTY git diff
|
|
204
|
-
const pty = await ptyExec({
|
|
205
|
-
url: server.url,
|
|
206
|
-
capability: server.capability.capability,
|
|
207
|
-
cwd: root,
|
|
208
|
-
command: 'git diff -- README.md; echo MONACO_MARK_DONE',
|
|
209
|
-
});
|
|
210
|
-
assert.ok(pty.identity, 'workspace_identity required');
|
|
211
|
-
assert.equal(pty.identity.root, path.resolve(root));
|
|
212
|
-
assert.equal(pty.identity.workspace_id, server.workspace_id);
|
|
213
|
-
assert.ok(pty.sessionId || pty.identity.session_id);
|
|
214
|
-
assert.ok(
|
|
215
|
-
pty.output.includes(monacoMarker) || pty.output.includes('+##'),
|
|
216
|
-
`git diff missing Monaco edit.\nOUTPUT:\n${pty.output}`,
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
// Persist receipt fields on the test for the final report
|
|
220
|
-
globalThis.__AGENTSAM_FS_E2E__ = {
|
|
221
|
-
root,
|
|
222
|
-
workspace_id: server.workspace_id,
|
|
223
|
-
runtimeBaseUrl: `http://${server.host}:${server.port}`,
|
|
224
|
-
pty_session_id: pty.sessionId || pty.identity.session_id,
|
|
225
|
-
monaco_path: 'README.md',
|
|
226
|
-
monaco_marker: monacoMarker,
|
|
227
|
-
git_diff_snippet: pty.output.slice(0, 1200),
|
|
228
|
-
identity: pty.identity,
|
|
229
|
-
};
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it('PTY disk edit → adapter detects version change → stale Monaco save conflicts', async () => {
|
|
233
|
-
const read1 = await adapter.read('NOTES.md');
|
|
234
|
-
assert.equal(read1.ok, true);
|
|
235
|
-
const staleVersion = read1.version;
|
|
236
|
-
|
|
237
|
-
// External change (simulates PTY echo >> NOTES.md)
|
|
238
|
-
fs.appendFileSync(path.join(root, 'NOTES.md'), `\n${ptyMarker}\n`);
|
|
239
|
-
|
|
240
|
-
const read2 = await adapter.read('NOTES.md');
|
|
241
|
-
assert.equal(read2.ok, true);
|
|
242
|
-
assert.notEqual(read2.version, staleVersion);
|
|
243
|
-
assert.ok(read2.content.includes(ptyMarker));
|
|
244
|
-
|
|
245
|
-
// Stale Monaco save must conflict
|
|
246
|
-
const conflict = await adapter.write('NOTES.md', 'should not land\n', staleVersion, false);
|
|
247
|
-
assert.equal(conflict.ok, false);
|
|
248
|
-
assert.equal(conflict.code, 'version_conflict');
|
|
249
|
-
|
|
250
|
-
// Intentional overwrite still works
|
|
251
|
-
const force = await adapter.write('NOTES.md', `${read2.content}\nforced\n`, null, true);
|
|
252
|
-
assert.equal(force.ok, true);
|
|
253
|
-
|
|
254
|
-
globalThis.__AGENTSAM_FS_E2E__ = globalThis.__AGENTSAM_FS_E2E__ || {};
|
|
255
|
-
globalThis.__AGENTSAM_FS_E2E__.pty_path = 'NOTES.md';
|
|
256
|
-
globalThis.__AGENTSAM_FS_E2E__.pty_marker = ptyMarker;
|
|
257
|
-
globalThis.__AGENTSAM_FS_E2E__.stale_conflict = conflict;
|
|
258
|
-
globalThis.__AGENTSAM_FS_E2E__.external_version_before = staleVersion;
|
|
259
|
-
globalThis.__AGENTSAM_FS_E2E__.external_version_after = read2.version;
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it('filesystem write marks repository freshness stale (path-scoped)', () => {
|
|
263
|
-
const fresh = getRepositoryFreshness(path.resolve(root));
|
|
264
|
-
assert.equal(fresh.ok, true);
|
|
265
|
-
assert.ok(fresh.generation >= 1);
|
|
266
|
-
assert.ok(fresh.stale_paths.includes('README.md') || fresh.stale_paths.includes('NOTES.md'));
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
it('records final proof receipt JSON', () => {
|
|
270
|
-
const receipt = {
|
|
271
|
-
schema: 'agentsam.fs-e2e-receipt.v1',
|
|
272
|
-
complete: true,
|
|
273
|
-
...globalThis.__AGENTSAM_FS_E2E__,
|
|
274
|
-
authorization: {
|
|
275
|
-
binding: '127.0.0.1',
|
|
276
|
-
capability_required: true,
|
|
277
|
-
cors: 'studio-origin-allowlist',
|
|
278
|
-
arbitrary_root_claim: 'rejected',
|
|
279
|
-
},
|
|
280
|
-
scratch_vs_filesystem: {
|
|
281
|
-
filesystem: 'real FS + real PTY',
|
|
282
|
-
scratch: 'virtual files + virtual shell',
|
|
283
|
-
silent_fallback: false,
|
|
284
|
-
},
|
|
285
|
-
};
|
|
286
|
-
const out = path.join(root, '.agentsam-fs-e2e-receipt.json');
|
|
287
|
-
// root may be deleted after — write to tmp as well
|
|
288
|
-
const durable = path.join(os.tmpdir(), `agentsam-fs-e2e-receipt-${Date.now()}.json`);
|
|
289
|
-
fs.writeFileSync(durable, `${JSON.stringify(receipt, null, 2)}\n`);
|
|
290
|
-
assert.ok(fs.existsSync(durable));
|
|
291
|
-
console.log(`\nFS_E2E_RECEIPT=${durable}\n`);
|
|
292
|
-
globalThis.__AGENTSAM_FS_E2E_RECEIPT_PATH__ = durable;
|
|
293
|
-
});
|
|
294
|
-
});
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import test from 'node:test';
|
|
6
|
-
import { terminalExec } from '../../src/capabilities/terminal-exec.js';
|
|
7
|
-
import { createCapabilityAdapter } from '../../src/agent/capability-adapter.js';
|
|
8
|
-
import { toolApprovalKey } from '../../src/lib/execution-approvals.js';
|
|
9
|
-
|
|
10
|
-
function root(t) {
|
|
11
|
-
const value = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-terminal-exec-'));
|
|
12
|
-
t.after(() => fs.rmSync(value, { recursive: true, force: true }));
|
|
13
|
-
return value;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
test('terminal.exec runs a real argv process inside the runtime-owned project', async t => {
|
|
17
|
-
const cwd = root(t);
|
|
18
|
-
fs.mkdirSync(path.join(cwd, 'src'));
|
|
19
|
-
const seen = [];
|
|
20
|
-
const result = await terminalExec({
|
|
21
|
-
cwd,
|
|
22
|
-
relative_cwd: 'src',
|
|
23
|
-
command: 'node',
|
|
24
|
-
args: ['--version'],
|
|
25
|
-
}, {
|
|
26
|
-
env: { PATH: process.env.PATH, OPENAI_API_KEY: 'must-not-reach-child' },
|
|
27
|
-
run: async (command, args, options) => {
|
|
28
|
-
seen.push({ command, args, options });
|
|
29
|
-
return { code: 0, signal: null, stdout: 'v24.0.0\n', stderr: '' };
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
assert.equal(result.ok, true);
|
|
33
|
-
assert.equal(result.cwd, path.join(cwd, 'src'));
|
|
34
|
-
assert.equal(seen[0].options.env.OPENAI_API_KEY, undefined);
|
|
35
|
-
assert.equal(seen[0].options.env.PATH, process.env.PATH);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test('terminal.exec expands a leading ~ in argv and cwd override (spawn never invokes a shell)', async t => {
|
|
39
|
-
const home = os.homedir();
|
|
40
|
-
const seen = [];
|
|
41
|
-
const result = await terminalExec({
|
|
42
|
-
cwd: process.cwd(),
|
|
43
|
-
command: 'git',
|
|
44
|
-
args: ['-C', '~/agentsam-sdk', 'status'],
|
|
45
|
-
}, {
|
|
46
|
-
env: { PATH: process.env.PATH },
|
|
47
|
-
run: async (command, args, options) => {
|
|
48
|
-
seen.push({ command, args, options });
|
|
49
|
-
return { code: 0, signal: null, stdout: '', stderr: '' };
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
assert.equal(result.ok, true);
|
|
53
|
-
assert.equal(seen[0].args[1], path.join(home, 'agentsam-sdk'));
|
|
54
|
-
assert.equal(seen[0].args[1].startsWith('~'), false);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test('terminal.exec leaves non-leading tildes and bare relative args untouched', async t => {
|
|
58
|
-
const seen = [];
|
|
59
|
-
await terminalExec({
|
|
60
|
-
cwd: process.cwd(),
|
|
61
|
-
command: 'echo',
|
|
62
|
-
args: ['user~name', 'plain-arg'],
|
|
63
|
-
}, {
|
|
64
|
-
env: { PATH: process.env.PATH },
|
|
65
|
-
run: async (command, args, options) => {
|
|
66
|
-
seen.push({ command, args, options });
|
|
67
|
-
return { code: 0, signal: null, stdout: '', stderr: '' };
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
assert.deepEqual(seen[0].args, ['user~name', 'plain-arg']);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('terminal.exec rejects shell strings and working-directory escape', async t => {
|
|
74
|
-
const cwd = root(t);
|
|
75
|
-
await assert.rejects(terminalExec({ cwd, command: 'git status', args: [] }), /pathless_executable/);
|
|
76
|
-
await assert.rejects(terminalExec({ cwd, relative_cwd: '..', command: 'git', args: ['status'] }), /outside_project/);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test('terminal.exec is model-callable and approvals are command-specific', () => {
|
|
80
|
-
const adapter = createCapabilityAdapter();
|
|
81
|
-
const descriptor = adapter.toolDescriptors().find((row) => row.name === 'terminal.exec');
|
|
82
|
-
assert.ok(descriptor);
|
|
83
|
-
assert.equal(descriptor.risk, 'write');
|
|
84
|
-
assert.equal(toolApprovalKey('terminal.exec', { command: 'git' }), 'terminal.exec:git');
|
|
85
|
-
assert.equal(toolApprovalKey('terminal.exec', { command: 'npm' }), 'terminal.exec:npm');
|
|
86
|
-
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import {
|
|
4
|
-
listThemePackages,
|
|
5
|
-
resolveThemePackage,
|
|
6
|
-
} from "../../packages/theme-scenes/src/registry.js";
|
|
7
|
-
|
|
8
|
-
test("theme registry resolves donor aliases to neutral canonical ids", () => {
|
|
9
|
-
const companions = resolveThemePackage("companions-of-caddo");
|
|
10
|
-
assert.equal(companions?.id, "violet");
|
|
11
|
-
assert.equal(companions?.packageName, "@inneranimalmedia/theme-violet");
|
|
12
|
-
assert.equal(companions?.packageNameLegacy, "@inneranimalmedia/theme-companions-site");
|
|
13
|
-
|
|
14
|
-
const fuel = resolveThemePackage("fuelnfreetime");
|
|
15
|
-
assert.equal(fuel?.id, "ember");
|
|
16
|
-
assert.equal(fuel?.packageName, "@inneranimalmedia/theme-ember");
|
|
17
|
-
|
|
18
|
-
const cypress = resolveThemePackage("cypress");
|
|
19
|
-
assert.equal(cypress?.path, "packages/theme-church-site");
|
|
20
|
-
|
|
21
|
-
const scenes = resolveThemePackage("theme-scenes");
|
|
22
|
-
assert.equal(scenes?.kind, "scenes");
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test("site themes remain distinct packages (not flattened)", () => {
|
|
26
|
-
const sites = listThemePackages("site-theme");
|
|
27
|
-
assert.ok(sites.length >= 7);
|
|
28
|
-
assert.ok(sites.every((entry) => entry.path.startsWith("packages/theme-")));
|
|
29
|
-
assert.ok(sites.every((entry) => entry.packageName.startsWith("@inneranimalmedia/theme-")));
|
|
30
|
-
assert.ok(sites.every((entry) => !entry.packageName.includes("-site") || entry.id === "docs"));
|
|
31
|
-
});
|