@inneranimalmedia/agentsam-sdk 2.6.8 → 2.6.10
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 +18 -0
- package/apps/README.md +25 -0
- package/apps/cad-creator/shared/cad/package.json +1 -1
- package/apps/client-cms-editor/backend/package.json +5 -7
- package/apps/client-cms-editor/frontend/package.json +5 -7
- package/apps/client-cms-editor/frontend/tsconfig.json +27 -8
- package/apps/client-cms-editor/package-lock.json +8 -8
- package/apps/client-cms-editor/package.json +1 -1
- package/apps/client-cms-editor/shared/cms/package.json +3 -5
- package/docs/LOCAL_ENGINE_COMPUTE_AUDIT_PLAN.md +299 -0
- package/docs/architecture/HOOKS-MODELS-SELF-HOSTING-PLAN.md +199 -0
- package/examples/hyperspace-demo/index.html +16 -0
- package/examples/hyperspace-demo/package.json +21 -0
- package/examples/hyperspace-demo/src/main.tsx +160 -0
- package/examples/hyperspace-demo/tsconfig.json +9 -0
- package/examples/hyperspace-demo/vite.config.ts +24 -0
- package/migrations/runtime/0005_agentsam_hooks.sql +87 -0
- package/package.json +37 -10
- package/packages/agentsam-brand/package.json +1 -1
- package/packages/agentsam-brand/src/core/v2/compile.js +1 -1
- package/packages/agentsam-brand/src/core/v2/schema.js +1 -1
- package/packages/agentsam-contracts/package.json +71 -20
- package/packages/agentsam-contracts/scripts/build.mjs +18 -0
- package/packages/agentsam-contracts/src/errors.ts +19 -10
- package/packages/agentsam-contracts/test/contracts.test.mjs +4 -1
- package/packages/agentsam-contracts/test/repository.test.mjs +4 -1
- package/packages/agentsam-contracts/tsconfig.build.json +10 -0
- package/packages/agentsam-errors/package.json +13 -4
- package/packages/agentsam-errors/protocol/agentsam.error.v2.schema.json +821 -0
- package/packages/agentsam-errors/protocol/error-catalog.json +3512 -0
- package/packages/agentsam-errors/src/envelope.js +63 -11
- package/packages/agentsam-errors/src/error.js +6 -0
- package/packages/agentsam-errors/src/fingerprint.js +9 -1
- package/packages/agentsam-errors/src/normalize.js +51 -6
- package/packages/agentsam-errors/src/recovery.js +9 -35
- package/packages/agentsam-errors/src/transport.js +6 -1
- package/packages/agentsam-errors/src/vocabulary.js +515 -126
- package/packages/agentsam-errors/test/catalog-v2.test.mjs +106 -0
- package/packages/agentsam-errors/types/index.d.ts +157 -0
- package/packages/agentsam-goap/package.json +2 -2
- package/packages/agentsam-hooks/README.md +206 -0
- package/packages/agentsam-hooks/bin/agentsam-hooks.mjs +64 -0
- package/packages/agentsam-hooks/bindings/go/command.go +55 -0
- package/packages/agentsam-hooks/bindings/go/errors_generated.go +402 -0
- package/packages/agentsam-hooks/bindings/go/go.mod +3 -0
- package/packages/agentsam-hooks/bindings/go/hooks.go +285 -0
- package/packages/agentsam-hooks/bindings/go/hooks_test.go +48 -0
- package/packages/agentsam-hooks/bindings/rust/Cargo.toml +11 -0
- package/packages/agentsam-hooks/bindings/rust/src/errors_generated.rs +396 -0
- package/packages/agentsam-hooks/bindings/rust/src/lib.rs +397 -0
- package/packages/agentsam-hooks/package.json +69 -0
- package/packages/agentsam-hooks/schema/manifest.json +54 -0
- package/packages/agentsam-hooks/scripts/clean-protocol.mjs +17 -0
- package/packages/agentsam-hooks/scripts/sync-protocol.mjs +27 -0
- package/packages/agentsam-hooks/src/adapters/agents.js +82 -0
- package/packages/agentsam-hooks/src/adapters/callback.js +4 -0
- package/packages/agentsam-hooks/src/adapters/capability.js +122 -0
- package/packages/agentsam-hooks/src/adapters/command.js +104 -0
- package/packages/agentsam-hooks/src/adapters/composite.js +31 -0
- package/packages/agentsam-hooks/src/adapters/http.js +58 -0
- package/packages/agentsam-hooks/src/adapters/lsp.js +116 -0
- package/packages/agentsam-hooks/src/adapters/mcp.js +76 -0
- package/packages/agentsam-hooks/src/adapters/provider.js +129 -0
- package/packages/agentsam-hooks/src/config.js +143 -0
- package/packages/agentsam-hooks/src/contracts.js +284 -0
- package/packages/agentsam-hooks/src/errors-generated.js +2732 -0
- package/packages/agentsam-hooks/src/index.d.ts +193 -0
- package/packages/agentsam-hooks/src/index.js +14 -0
- package/packages/agentsam-hooks/src/policy.js +34 -0
- package/packages/agentsam-hooks/src/runtime.js +312 -0
- package/packages/agentsam-hooks/src/store.js +264 -0
- package/packages/agentsam-hooks/test/adapters.test.mjs +216 -0
- package/packages/agentsam-hooks/test/runtime.test.mjs +258 -0
- package/packages/agentsam-knowledge/package.json +3 -2
- package/packages/agentsam-nav/package.json +1 -1
- package/packages/agentsam-queue-control/package.json +1 -1
- package/packages/agentsam-rapid-rust/src/main.rs +1 -0
- package/packages/agentsam-repository/package.json +3 -2
- package/packages/agentsam-vault/package.json +8 -5
- package/packages/agentsam-workbench/package.json +45 -18
- package/packages/connectors/cloudflare/package.json +1 -1
- package/packages/heuristic-theme/package.json +1 -1
- package/packages/identity/package.json +3 -2
- package/packages/identity/tests/lane1-packed-consumer.test.mjs +9 -1
- package/packages/providers/completeful/package.json +1 -1
- package/packages/theme-church-site/README.md +2 -2
- package/packages/theme-church-site/package.json +2 -3
- package/packages/theme-church-site/src/index.js +1 -1
- package/packages/theme-church-site/test/theme.test.mjs +1 -1
- package/packages/theme-companions-site/README.md +2 -2
- package/packages/theme-companions-site/package.json +2 -3
- package/packages/theme-companions-site/src/index.js +1 -1
- package/packages/theme-companions-site/test/theme.test.mjs +1 -1
- package/packages/theme-floors-site/README.md +2 -2
- package/packages/theme-floors-site/package.json +2 -3
- package/packages/theme-floors-site/src/index.js +1 -1
- package/packages/theme-floors-site/test/theme.test.mjs +1 -1
- package/packages/theme-fuelnfree-site/README.md +2 -2
- package/packages/theme-fuelnfree-site/package.json +2 -3
- package/packages/theme-fuelnfree-site/src/index.js +1 -1
- package/packages/theme-fuelnfree-site/test/theme.test.mjs +1 -1
- package/packages/theme-handyman-site/README.md +2 -2
- package/packages/theme-handyman-site/package.json +2 -3
- package/packages/theme-handyman-site/src/index.js +1 -1
- package/packages/theme-handyman-site/test/theme.test.mjs +1 -1
- package/packages/theme-insurance-site/README.md +2 -2
- package/packages/theme-insurance-site/package.json +2 -3
- package/packages/theme-insurance-site/src/index.js +1 -1
- package/packages/theme-insurance-site/test/theme.test.mjs +1 -1
- package/packages/theme-scenes/package.json +1 -1
- package/packages/theme-shinshu-site/README.md +2 -2
- package/packages/theme-shinshu-site/package.json +2 -3
- package/packages/theme-shinshu-site/src/index.js +1 -1
- package/packages/theme-shinshu-site/test/theme.test.mjs +1 -1
- package/packages/work-graph/package.json +3 -2
- package/protocol/apps/agentsam.app.v1.schema.json +1 -1
- package/protocol/apps/agentsam.host-install.v1.schema.json +1 -1
- package/protocol/assets/asset-record.v1.schema.json +1 -1
- package/protocol/brand/brandpack.v1.schema.json +1 -1
- package/protocol/capabilities/capability-manifest.schema.json +1 -1
- package/protocol/capabilities/knowledge-search-input.schema.json +1 -0
- package/protocol/credentials/issue.v1.schema.json +1 -1
- package/protocol/database/agentsam.schema-pack.v1.schema.json +1 -1
- package/protocol/database/connection.v1.schema.json +1 -1
- package/protocol/embeddings/embedding-profile.v1.schema.json +1 -1
- package/protocol/errors/README.md +36 -0
- package/protocol/errors/agentsam.error.v2.schema.json +821 -0
- package/protocol/errors/error-catalog.json +3506 -158
- package/protocol/errors/error-envelope.schema.json +195 -52
- package/protocol/errors/recovery.v1.schema.json +1 -1
- package/protocol/features/agentsam.feature.v1.schema.json +1 -1
- package/protocol/hooks/README.md +18 -0
- package/protocol/hooks/agentsam.hook.output.v1.schema.json +27 -0
- package/protocol/hooks/agentsam.hook.receipt.v1.schema.json +61 -0
- package/protocol/hooks/agentsam.hook.v1.schema.json +35 -0
- package/protocol/hooks/agentsam.hooks.config.v1.schema.json +78 -0
- package/protocol/local-engine/benchmark.v1.schema.json +14 -0
- package/protocol/local-engine/engine.v1.schema.json +17 -0
- package/protocol/models/model-inventory-v2.schema.json +1 -1
- package/protocol/models/provider-model-snapshot.v1.schema.json +22 -0
- package/protocol/rpc/v1/errors.proto +1 -0
- package/protocol/runtime/agentsam.runtime.v1.schema.json +1 -1
- package/protocol/runtime/workspace-fs.v1.schema.json +1 -1
- package/protocol/sam/activity.v1.schema.json +1 -1
- package/protocol/sam/answer.v1.schema.json +1 -1
- package/protocol/sam/calibration.v1.schema.json +1 -1
- package/protocol/sam/decision-receipt.v1.schema.json +1 -1
- package/protocol/sam/evaluation.v1.schema.json +1 -1
- package/protocol/sam/operation.schema.json +1 -1
- package/protocol/sam/outcome.v1.schema.json +1 -1
- package/protocol/sam/question.v1.schema.json +1 -1
- package/protocol/sam/result.schema.json +1 -1
- package/protocol/sam/state.v1.schema.json +1 -1
- package/protocol/scoring/scorecard.v1.schema.json +1 -1
- package/protocol/skills/agentsam.interaction.v1.schema.json +1 -1
- package/protocol/skills/agentsam.skill.v1.schema.json +1 -1
- package/protocol/theme-refinery/product-metadata.schema.json +1 -1
- package/protocol/theme-refinery/reconciliation-receipt.schema.json +1 -1
- package/protocol/ui/icon.v1.schema.json +1 -1
- package/python/README.md +20 -0
- package/python/agentsam_sdk/errors/__init__.py +4 -0
- package/python/agentsam_sdk/errors/vocabulary.py +2730 -0
- package/python/agentsam_sdk/hooks/__init__.py +32 -0
- package/python/agentsam_sdk/hooks/adapters.py +132 -0
- package/python/agentsam_sdk/hooks/contracts.py +121 -0
- package/python/agentsam_sdk/hooks/runtime.py +253 -0
- package/python/tests/test_hooks.py +115 -0
- package/src/agent/responses-runner.js +145 -1
- package/src/cli/command-catalog.js +2 -0
- package/src/cli.js +19 -0
- package/src/commands/app.js +10 -0
- package/src/commands/cheat-sheet.js +1 -0
- package/src/commands/engine.js +10 -0
- package/src/commands/hooks.js +260 -0
- package/src/commands/machine.js +71 -38
- package/src/commands/models.js +1 -1
- package/src/commands/package.js +10 -3
- package/src/commands/runtime.js +25 -10
- package/src/commands/setup.js +30 -6
- package/src/commands/shell.js +114 -9
- package/src/commands/terminal.js +20 -8
- package/src/errors/contract.js +4 -4
- package/src/errors/diagnostic.js +10 -10
- package/src/errors/index.js +1 -1
- package/src/index.js +52 -0
- package/src/indexing/ingest/materials.js +237 -30
- package/src/knowledge/service/grpc-codec.js +2 -0
- package/src/lib/setup/runtime.js +4 -2
- package/src/lib/slash-commands.js +1 -0
- package/src/local/runtime-store.js +46 -0
- package/src/local-engine/assets.js +37 -0
- package/src/local-engine/audit.js +133 -0
- package/src/local-engine/benchmark.js +26 -0
- package/src/local-engine/contracts.js +101 -0
- package/src/local-engine/engines.js +142 -0
- package/src/local-engine/index.js +18 -0
- package/src/local-engine/runtime.js +39 -0
- package/src/models/catalog.js +0 -49
- package/src/models/inventory-core.js +69 -0
- package/src/plugins/index.js +8 -1
- package/src/plugins/registry.js +56 -0
- package/src/rpc/generated/errors_pb.js +49 -1
- package/src/tools/runtime.js +13 -5
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import test from 'node:test';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import {
|
|
8
|
+
ERROR_REASON_POLICY,
|
|
9
|
+
FAILURE_CLASS,
|
|
10
|
+
canonicalizeErrorReason,
|
|
11
|
+
createErrorEnvelope,
|
|
12
|
+
normalizeError,
|
|
13
|
+
planRecovery,
|
|
14
|
+
} from '../src/index.js';
|
|
15
|
+
|
|
16
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..');
|
|
17
|
+
const catalog = JSON.parse(fs.readFileSync(path.join(root, 'protocol/errors/error-catalog.json'), 'utf8'));
|
|
18
|
+
|
|
19
|
+
test('v2 keeps broad codes stable while preserving adapter-specific timeout reasons', () => {
|
|
20
|
+
assert.equal(catalog.codes.length, 17);
|
|
21
|
+
assert.equal(catalog.schema_version, 2);
|
|
22
|
+
const cases = [
|
|
23
|
+
['http', 'AGENTSAM_HOOK_TIMEOUT', 'hook_http_timeout'],
|
|
24
|
+
['mcp', 'hook_mcp_timeout:server', 'hook_mcp_timeout'],
|
|
25
|
+
['lsp', 'hook_lsp_timeout:rust', 'hook_lsp_timeout'],
|
|
26
|
+
['command', 'hook_command_timeout:node', 'hook_command_timeout'],
|
|
27
|
+
];
|
|
28
|
+
for (const [adapter, native, reason] of cases) {
|
|
29
|
+
const error = normalizeError(Object.assign(new Error(native), { code: native.split(':')[0] }), {
|
|
30
|
+
adapter,
|
|
31
|
+
feature: `hooks.${adapter}`,
|
|
32
|
+
failure_behavior: 'fail_closed',
|
|
33
|
+
side_effect_state: 'not_started',
|
|
34
|
+
});
|
|
35
|
+
assert.equal(error.code, 'DEADLINE_EXCEEDED');
|
|
36
|
+
assert.equal(error.reason, reason);
|
|
37
|
+
assert.equal(error.adapter, adapter);
|
|
38
|
+
assert.equal(error.feature, `hooks.${adapter}`);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('v2 dimensions use generic stages and retain subsystem-native stages separately', () => {
|
|
43
|
+
const error = createErrorEnvelope({
|
|
44
|
+
reason: 'schema_contract_mismatch',
|
|
45
|
+
stage: 'constraint_solve',
|
|
46
|
+
feature: 'database.migrate',
|
|
47
|
+
failure_behavior: 'abort',
|
|
48
|
+
side_effect_state: 'confirmed_not_applied',
|
|
49
|
+
message: 'portable schema ownership differs',
|
|
50
|
+
});
|
|
51
|
+
assert.equal(error.domain, 'schema');
|
|
52
|
+
assert.equal(error.failure_class, 'schema');
|
|
53
|
+
assert.equal(error.stage, 'validate');
|
|
54
|
+
assert.equal(error.native_stage, 'constraint_solve');
|
|
55
|
+
assert.equal(error.feature, 'database.migrate');
|
|
56
|
+
assert.equal(error.side_effect_state, 'confirmed_not_applied');
|
|
57
|
+
assert.throws(() => createErrorEnvelope({ reason: 'input_invalid', feature: 'Not a dotted ID' }), /Invalid AgentSam error feature/);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('recovery consumes the generated failure and side-effect vocabularies', () => {
|
|
61
|
+
assert.equal(FAILURE_CLASS.VALIDATION, 'validation');
|
|
62
|
+
const error = createErrorEnvelope({
|
|
63
|
+
reason: 'input_invalid', message: 'bad input', side_effect_state: 'confirmed_not_applied',
|
|
64
|
+
});
|
|
65
|
+
const recovery = planRecovery(error);
|
|
66
|
+
assert.equal(recovery.disposition, 'await_user');
|
|
67
|
+
assert.equal(recovery.reason, 'invalid_input');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('every harvested legacy hook alias resolves to its catalog reason', () => {
|
|
71
|
+
for (const row of catalog.reasons.filter((reason) => reason.domain === 'hook' || reason.aliases?.some((alias) => /hook|mcp|lsp|capability|agent/i.test(alias)))) {
|
|
72
|
+
for (const alias of row.aliases || []) {
|
|
73
|
+
assert.equal(canonicalizeErrorReason(`${alias}:native detail`), row.name, alias);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('generated JS, Go, Rust, Python, and packed catalog policies retain parity', () => {
|
|
79
|
+
const packedCatalog = JSON.parse(fs.readFileSync(path.join(root, 'packages/agentsam-errors/protocol/error-catalog.json'), 'utf8'));
|
|
80
|
+
assert.deepEqual(packedCatalog, catalog);
|
|
81
|
+
const expected = Object.fromEntries(catalog.reasons.map((row) => [row.name, {
|
|
82
|
+
code: row.code,
|
|
83
|
+
domain: row.domain,
|
|
84
|
+
failure_class: row.failure_class,
|
|
85
|
+
severity: row.severity,
|
|
86
|
+
retryable: row.retryable,
|
|
87
|
+
resolution_owner: row.resolution_owner,
|
|
88
|
+
remediation_action: row.remediation_action,
|
|
89
|
+
default_stage: row.default_stage,
|
|
90
|
+
}]));
|
|
91
|
+
assert.deepEqual(ERROR_REASON_POLICY, expected);
|
|
92
|
+
|
|
93
|
+
const python = JSON.parse(execFileSync('python3', ['-c', [
|
|
94
|
+
'import json',
|
|
95
|
+
'from agentsam_sdk.errors import ERROR_REASON_POLICY',
|
|
96
|
+
'print(json.dumps(ERROR_REASON_POLICY, sort_keys=True))',
|
|
97
|
+
].join(';')], { cwd: root, env: { ...process.env, PYTHONPATH: path.join(root, 'python') }, encoding: 'utf8' }));
|
|
98
|
+
assert.deepEqual(python, expected);
|
|
99
|
+
|
|
100
|
+
const go = fs.readFileSync(path.join(root, 'packages/agentsam-hooks/bindings/go/errors_generated.go'), 'utf8');
|
|
101
|
+
const rust = fs.readFileSync(path.join(root, 'packages/agentsam-hooks/bindings/rust/src/errors_generated.rs'), 'utf8');
|
|
102
|
+
for (const row of catalog.reasons) {
|
|
103
|
+
assert.ok(go.includes(`${JSON.stringify(row.name)}: {Code: ${JSON.stringify(row.code)}`), `Go policy missing ${row.name}`);
|
|
104
|
+
assert.ok(rust.includes(`reason: ${JSON.stringify(row.name)}, code: ${JSON.stringify(row.code)}`), `Rust policy missing ${row.name}`);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// GENERATED by scripts/generate-error-contracts.mjs. Do not edit by hand.
|
|
2
|
+
|
|
3
|
+
export const ERROR_SCHEMA_VERSION: 2;
|
|
4
|
+
|
|
5
|
+
export type AgentSamErrorCode = "OK" | "CANCELLED" | "UNKNOWN" | "INVALID_ARGUMENT" | "DEADLINE_EXCEEDED" | "NOT_FOUND" | "ALREADY_EXISTS" | "PERMISSION_DENIED" | "RESOURCE_EXHAUSTED" | "FAILED_PRECONDITION" | "ABORTED" | "OUT_OF_RANGE" | "UNIMPLEMENTED" | "INTERNAL" | "UNAVAILABLE" | "DATA_LOSS" | "UNAUTHENTICATED";
|
|
6
|
+
export type AgentSamErrorReason = "unknown" | "input_invalid" | "input_out_of_range" | "precondition_failed" | "unsupported_operation" | "auth_missing" | "auth_invalid" | "permission_denied" | "user_permission_denied" | "provider_not_connected" | "provider_credential_missing" | "provider_credential_invalid" | "provider_credential_expired" | "provider_scope_insufficient" | "provider_budget_exhausted" | "provider_quota_exhausted" | "provider_rate_limited" | "provider_capacity_exhausted" | "provider_unavailable" | "provider_overloaded" | "provider_request_invalid" | "provider_model_not_found" | "provider_region_unsupported" | "provider_content_blocked" | "context_window_exceeded" | "service_tier_unsupported" | "continuation_invalid" | "target_not_found" | "already_exists" | "conflict" | "stale_version" | "rate_limited" | "quota_exhausted" | "capacity_exhausted" | "transport_unreachable" | "transport_refused" | "transport_timeout" | "upstream_invalid_response" | "dns_unresolved" | "tls_handshake_failed" | "tls_certificate_invalid" | "tunnel_connector_unavailable" | "cancelled" | "deadline_exceeded" | "execution_timeout" | "execution_failed" | "process_spawn_failed" | "process_crashed" | "process_output_limit" | "device_not_enrolled" | "device_credential_missing" | "device_credential_invalid" | "device_credential_expired" | "device_registration_missing" | "device_registration_stale" | "connection_revoked" | "connection_disabled" | "identity_resolution_failed" | "identity_mismatch" | "repository_scope_violation" | "repository_changed" | "index_stale" | "index_failed" | "embedding_failed" | "vector_store_failed" | "github_installation_missing" | "github_repository_not_found" | "github_rate_limited" | "github_secondary_rate_limited" | "github_scope_insufficient" | "cloudflare_workers_cpu_exhausted" | "cloudflare_service_binding_unavailable" | "cloudflare_durable_object_unavailable" | "cloudflare_d1_unavailable" | "cloudflare_r2_unavailable" | "docker_daemon_unavailable" | "docker_image_not_found" | "docker_build_failed" | "docker_container_failed" | "docker_registry_auth_failed" | "docker_pull_rate_limited" | "gcp_project_missing" | "gcp_api_disabled" | "gcp_quota_exhausted" | "gcp_vm_not_found" | "gcp_vm_unavailable" | "cad_tool_not_installed" | "cad_tool_not_executable" | "cad_tool_version_incompatible" | "cad_component_missing" | "cad_dependency_missing" | "cad_dependency_version_incompatible" | "cad_source_parse_failed" | "cad_input_format_unsupported" | "cad_import_failed" | "cad_model_recompute_failed" | "cad_constraint_unsatisfied" | "cad_geometry_invalid" | "cad_null_shape" | "cad_boolean_failed" | "cad_kernel_failed" | "cad_script_failed" | "cad_export_format_unsupported" | "cad_export_failed" | "cad_output_missing" | "cad_output_invalid" | "cad_render_engine_unavailable" | "cad_gpu_unavailable" | "cad_render_failed" | "cad_memory_exhausted" | "cad_process_crashed" | "cad_warning_escalated" | "policy_blocked" | "copyright_blocked" | "persistence_failed" | "data_corrupt" | "data_loss" | "internal" | "internal_invariant_violation" | "internal_configuration_invalid" | "internal_contract_violation" | "internal_adapter_failed" | "internal_dependency_failed" | "hook_definition_invalid" | "hook_duplicate_id" | "hook_not_found" | "hook_disabled" | "hook_event_unsupported" | "hook_configuration_invalid" | "hook_adapter_unknown" | "hook_adapter_unavailable" | "hook_handler_unavailable" | "hook_handler_timeout" | "hook_handler_failed" | "hook_output_invalid" | "hook_output_limit_exceeded" | "hook_output_field_unsupported" | "hook_http_timeout" | "hook_http_request_failed" | "hook_http_response_invalid" | "hook_http_response_limit_exceeded" | "hook_http_protocol_unsupported" | "hook_http_credentials_forbidden" | "hook_mcp_timeout" | "hook_mcp_server_unavailable" | "hook_mcp_capability_missing" | "hook_mcp_capability_duplicate" | "hook_mcp_response_invalid" | "hook_mcp_transport_failed" | "hook_lsp_timeout" | "hook_lsp_server_unavailable" | "hook_lsp_language_required" | "hook_lsp_capability_unsupported" | "hook_lsp_initialize_failed" | "hook_lsp_response_invalid" | "hook_command_required" | "hook_command_timeout" | "hook_command_spawn_failed" | "hook_command_exit_nonzero" | "hook_command_output_limit_exceeded" | "hook_command_output_invalid" | "hook_permission_denied" | "hook_capability_unavailable" | "hook_capability_duplicate" | "hook_error_handling_invalid" | "hook_error_handling_unsupported" | "hook_retry_limit_exceeded" | "hook_stop_decision_invalid" | "package_manifest_invalid" | "package_export_missing" | "package_file_missing" | "package_unwanted_file_included" | "package_dependency_missing" | "package_dependency_version_mismatch" | "lockfile_missing" | "lockfile_stale" | "lockfile_incompatible" | "workspace_not_installed" | "workspace_resolution_failed" | "workspace_boundary_invalid" | "build_dependency_missing" | "build_failed" | "build_output_missing" | "build_output_stale" | "pack_failed" | "pack_contents_invalid" | "pack_provenance_violation" | "distribution_artifact_missing" | "distribution_identity_mismatch" | "distribution_version_mismatch" | "distribution_install_failed" | "distribution_smoke_failed" | "release_train_misaligned" | "published_artifact_stale" | "published_artifact_missing" | "schema_invalid" | "schema_version_unsupported" | "schema_contract_mismatch" | "schema_pack_missing" | "schema_pack_dependency_missing" | "schema_pack_dependency_cycle" | "schema_pack_ownership_conflict" | "migration_missing" | "migration_failed" | "migration_checksum_mismatch" | "migration_order_invalid" | "migration_already_applied_conflict" | "migration_requires_manual_repair" | "database_unreachable" | "database_driver_unsupported" | "database_authority_unresolved" | "database_authorization_failed" | "database_schema_drift" | "model_discovery_failed" | "model_catalog_stale" | "model_snapshot_missing" | "model_snapshot_stale" | "model_capability_unknown" | "model_capability_unsupported" | "model_provider_mismatch" | "model_identity_conflict" | "model_probe_failed" | "model_probe_partial" | "runtime_not_installed" | "runtime_not_running" | "runtime_version_incompatible" | "runtime_capability_missing" | "runtime_protocol_mismatch" | "runtime_connection_unresolved" | "runtime_connection_revoked" | "pty_create_failed" | "pty_attach_failed" | "pty_session_not_found" | "pty_session_expired" | "pty_replay_cursor_invalid" | "transport_not_configured" | "transport_auth_failed" | "transport_attach_failed" | "sidecar_missing" | "sidecar_start_failed" | "sidecar_health_failed" | "tool_not_found" | "tool_disabled" | "tool_handler_unavailable" | "tool_execution_failed" | "tool_timeout" | "tool_output_invalid" | "tool_permission_denied" | "mcp_timeout" | "mcp_server_unavailable" | "mcp_capability_unsupported" | "mcp_response_invalid" | "mcp_transport_failed" | "lsp_timeout" | "lsp_server_unavailable" | "lsp_capability_unsupported" | "lsp_response_invalid" | "lsp_transport_failed" | "mcp_call_failed" | "lsp_request_failed";
|
|
7
|
+
export type AgentSamErrorSeverity = "transient" | "blocking_user_fixable" | "blocking_internal" | "policy_blocking";
|
|
8
|
+
export type AgentSamErrorSourceKind = "user" | "agentsam" | "provider" | "dependency" | "runtime" | "policy";
|
|
9
|
+
export type AgentSamErrorResolutionOwner = "user" | "agentsam" | "provider" | "organization_admin" | "policy" | "unknown";
|
|
10
|
+
export type AgentSamErrorDomain = "runtime" | "provider" | "auth" | "identity" | "device" | "terminal" | "transport" | "repository" | "indexing" | "knowledge" | "cad" | "hook" | "model" | "configuration" | "schema" | "migration" | "database" | "package" | "build" | "distribution" | "deployment" | "workflow" | "persistence" | "policy" | "tool";
|
|
11
|
+
export type AgentSamErrorFailureClass = "input" | "validation" | "authentication" | "authorization" | "policy" | "discovery" | "resolution" | "availability" | "transport" | "protocol" | "timeout" | "rate_limit" | "capacity" | "resource" | "configuration" | "dependency" | "filesystem" | "persistence" | "consistency" | "conflict" | "process" | "ipc" | "data_integrity" | "cancellation" | "cancelled" | "build" | "distribution" | "migration" | "schema" | "identity" | "execution" | "internal" | "unknown";
|
|
12
|
+
export type AgentSamErrorStage = "discover" | "resolve" | "load" | "configure" | "validate" | "authorize" | "connect" | "prepare" | "install" | "build" | "start" | "execute" | "transform" | "persist" | "migrate" | "verify" | "publish" | "attach" | "detach" | "cleanup" | "shutdown";
|
|
13
|
+
export type AgentSamErrorFailureBehavior = "fail_closed" | "fail_open" | "observe_only" | "retry_bounded" | "skip" | "abort" | "no_replay";
|
|
14
|
+
export type AgentSamRemediationAction = "none" | "retry" | "retry_later" | "reauthenticate" | "connect_provider" | "configure_provider_credential" | "refresh_provider_credential" | "request_permission" | "reenroll_device" | "reconnect_service" | "install_dependency" | "upgrade_dependency" | "configure_dependency" | "change_configuration" | "change_input" | "reduce_scope" | "increase_quota" | "add_budget" | "select_model" | "change_region" | "restart_runtime" | "inspect_platform" | "contact_provider" | "contact_agentsam" | "contact_organization_admin" | "restart_service" | "repair_schema" | "run_migration" | "regenerate_artifact" | "update_lockfile" | "reinstall_workspace" | "republish_artifact";
|
|
15
|
+
export type AgentSamErrorSideEffectState = "none" | "not_started" | "confirmed_not_applied" | "confirmed_applied" | "partially_applied" | "unknown";
|
|
16
|
+
export type AgentSamErrorOutcomeStatus = "success" | "partial" | "failure";
|
|
17
|
+
|
|
18
|
+
export interface AgentSamErrorSource { kind: AgentSamErrorSourceKind; name: string; service: string | null; }
|
|
19
|
+
export interface AgentSamErrorRemediation { action: AgentSamRemediationAction; message: string | null; automatic: boolean; command: string | null; url: string | null; }
|
|
20
|
+
export interface AgentSamErrorResource { type: string; id: string | null; name: string | null; }
|
|
21
|
+
export interface AgentSamNativeErrorEvidence { code: string | null; exception_type: string | null; exit_code: number | null; signal: string | null; stderr: string | null; stdout: string | null; stack: string | null; }
|
|
22
|
+
export interface AgentSamEnvironmentEvidence { detected_version: string | null; required_version: string | null; component: string | null; runtime: string | null; platform: string | null; }
|
|
23
|
+
export interface AgentSamErrorOperation { kind?: string | null; action?: string | null; resource_type?: string | null; resource_id?: string | null; read_only?: boolean; idempotent?: boolean; side_effect_state?: AgentSamErrorSideEffectState; }
|
|
24
|
+
export interface AgentSamErrorRetry { allowed?: boolean; strategy?: string; retry_after_ms?: number | null; max_attempts?: number | null; requires_idempotency_key?: boolean; }
|
|
25
|
+
export interface AgentSamErrorFallback { allowed?: boolean; strategy?: string | null; preserve_semantics?: boolean; target?: string | null; }
|
|
26
|
+
export interface AgentSamErrorNotification { user?: string; operator?: string; agent?: string; }
|
|
27
|
+
export interface AgentSamErrorPartialResult { completed?: number; failed?: number; failures?: unknown[]; }
|
|
28
|
+
|
|
29
|
+
export interface AgentSamErrorEnvelope {
|
|
30
|
+
ok: false;
|
|
31
|
+
schema_version: typeof ERROR_SCHEMA_VERSION;
|
|
32
|
+
code: AgentSamErrorCode;
|
|
33
|
+
reason: AgentSamErrorReason | (string & {});
|
|
34
|
+
domain: AgentSamErrorDomain;
|
|
35
|
+
failure_class: AgentSamErrorFailureClass;
|
|
36
|
+
stage: AgentSamErrorStage;
|
|
37
|
+
native_stage: string | null;
|
|
38
|
+
feature: string | null;
|
|
39
|
+
failure_behavior: AgentSamErrorFailureBehavior | null;
|
|
40
|
+
severity: AgentSamErrorSeverity;
|
|
41
|
+
retryable: boolean;
|
|
42
|
+
side_effect_state: AgentSamErrorSideEffectState;
|
|
43
|
+
resolution_owner: AgentSamErrorResolutionOwner;
|
|
44
|
+
source: AgentSamErrorSource;
|
|
45
|
+
adapter: string | null;
|
|
46
|
+
protocol: string | null;
|
|
47
|
+
transport: string | null;
|
|
48
|
+
tool: string | null;
|
|
49
|
+
message: string;
|
|
50
|
+
retry_after_ms: number | null;
|
|
51
|
+
remediation: AgentSamErrorRemediation;
|
|
52
|
+
resource: AgentSamErrorResource | null;
|
|
53
|
+
native: AgentSamNativeErrorEvidence | null;
|
|
54
|
+
environment: AgentSamEnvironmentEvidence | null;
|
|
55
|
+
http_status: number | null;
|
|
56
|
+
grpc_status: number | null;
|
|
57
|
+
provider: string | null;
|
|
58
|
+
provider_code: string | null;
|
|
59
|
+
request_id: string | null;
|
|
60
|
+
trace_id: string | null;
|
|
61
|
+
fingerprint: string;
|
|
62
|
+
occurrence_count: number;
|
|
63
|
+
details: unknown;
|
|
64
|
+
operation?: AgentSamErrorOperation;
|
|
65
|
+
retry?: AgentSamErrorRetry;
|
|
66
|
+
fallback?: AgentSamErrorFallback;
|
|
67
|
+
notify?: AgentSamErrorNotification;
|
|
68
|
+
cause?: unknown;
|
|
69
|
+
status?: AgentSamErrorOutcomeStatus;
|
|
70
|
+
partial?: AgentSamErrorPartialResult;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const ERROR_CODE: Readonly<Record<string, AgentSamErrorCode>>;
|
|
74
|
+
export const GRPC_STATUS: Readonly<Record<AgentSamErrorCode, number>>;
|
|
75
|
+
export const DEFAULT_HTTP_STATUS: Readonly<Record<AgentSamErrorCode, number>>;
|
|
76
|
+
export const ERROR_REASON: Readonly<Record<string, AgentSamErrorReason>>;
|
|
77
|
+
export const ERROR_SEVERITY: Readonly<Record<string, AgentSamErrorSeverity>>;
|
|
78
|
+
export const ERROR_SOURCE_KIND: Readonly<Record<string, AgentSamErrorSourceKind>>;
|
|
79
|
+
export const ERROR_RESOLUTION_OWNER: Readonly<Record<string, AgentSamErrorResolutionOwner>>;
|
|
80
|
+
export const ERROR_DOMAIN: Readonly<Record<string, AgentSamErrorDomain>>;
|
|
81
|
+
export const ERROR_FAILURE_CLASS: Readonly<Record<string, AgentSamErrorFailureClass>>;
|
|
82
|
+
export const ERROR_STAGE: Readonly<Record<string, AgentSamErrorStage>>;
|
|
83
|
+
export const ERROR_FAILURE_BEHAVIOR: Readonly<Record<string, AgentSamErrorFailureBehavior>>;
|
|
84
|
+
export const ERROR_SIDE_EFFECT_STATE: Readonly<Record<string, AgentSamErrorSideEffectState>>;
|
|
85
|
+
export const REMEDIATION_ACTION: Readonly<Record<string, AgentSamRemediationAction>>;
|
|
86
|
+
export const ERROR_REASON_POLICY: Readonly<Record<AgentSamErrorReason, { code: AgentSamErrorCode; domain: AgentSamErrorDomain; failure_class: AgentSamErrorFailureClass; severity: AgentSamErrorSeverity; retryable: boolean; resolution_owner: AgentSamErrorResolutionOwner; remediation_action: AgentSamRemediationAction; default_stage: AgentSamErrorStage }>>;
|
|
87
|
+
export const ERROR_REASON_ALIASES: Readonly<Record<string, AgentSamErrorReason>>;
|
|
88
|
+
|
|
89
|
+
export function createErrorEnvelope(input?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
90
|
+
export function isErrorEnvelope(value: unknown): value is AgentSamErrorEnvelope;
|
|
91
|
+
export function assertErrorEnvelope(value: unknown): Readonly<AgentSamErrorEnvelope>;
|
|
92
|
+
export function serializeError(value: unknown): string;
|
|
93
|
+
export function parseError(value: string | unknown): Readonly<AgentSamErrorEnvelope>;
|
|
94
|
+
export function canonicalCodeFromHttpStatus(status: number): AgentSamErrorCode;
|
|
95
|
+
export function canonicalCodeFromGrpcStatus(status: number): AgentSamErrorCode;
|
|
96
|
+
export function defaultHttpStatusForCode(code: AgentSamErrorCode): number;
|
|
97
|
+
export function grpcStatusForCode(code: AgentSamErrorCode): number;
|
|
98
|
+
export function canonicalizeErrorReason(value: unknown, context?: Record<string, unknown>): AgentSamErrorReason | null;
|
|
99
|
+
export function normalizeError(error: unknown, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
100
|
+
export function fingerprintError(value?: Record<string, unknown>): string;
|
|
101
|
+
export function redactString(value: unknown, maxChars?: number): string;
|
|
102
|
+
export function redactErrorValue(value: unknown, options?: Record<string, unknown>): unknown;
|
|
103
|
+
export function sanitizeNativeEvidence(value: unknown): AgentSamNativeErrorEvidence | null;
|
|
104
|
+
export function shouldRetry(error: unknown): boolean;
|
|
105
|
+
export function retryDelayMs(error: unknown, attempt?: number, options?: Record<string, unknown>): number;
|
|
106
|
+
export function renderError(error: unknown): string;
|
|
107
|
+
export function toHttpError(error: unknown): { status: number; headers: Readonly<Record<string, string>>; body: { error: AgentSamErrorEnvelope } };
|
|
108
|
+
export function fromHttpError(input?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
109
|
+
export function createToolErrorEnvelope(input?: Record<string, unknown>, options?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
110
|
+
export function createToolError(input?: Record<string, unknown>, options?: Record<string, unknown>): AgentSamError;
|
|
111
|
+
export function inferFailureClass(error?: Record<string, unknown>): string;
|
|
112
|
+
export function inferSideEffectState(error?: Record<string, unknown>, operationContext?: Record<string, unknown>): string;
|
|
113
|
+
export function planRecovery(error?: Record<string, unknown>, operationContext?: Record<string, unknown>): Record<string, unknown>;
|
|
114
|
+
export const FAILURE_CLASS: Readonly<Record<string, string>>;
|
|
115
|
+
export const SIDE_EFFECT_STATE: Readonly<Record<string, string>>;
|
|
116
|
+
export const RECOVERY_SCHEMA: 'agentsam.recovery.v1';
|
|
117
|
+
|
|
118
|
+
export class AgentSamError extends Error {
|
|
119
|
+
constructor(envelope: Record<string, unknown> | AgentSamErrorEnvelope, options?: { cause?: unknown });
|
|
120
|
+
readonly envelope: Readonly<AgentSamErrorEnvelope>;
|
|
121
|
+
readonly diagnostic: Readonly<AgentSamErrorEnvelope>;
|
|
122
|
+
readonly code: AgentSamErrorCode;
|
|
123
|
+
readonly reason: string;
|
|
124
|
+
readonly domain: AgentSamErrorDomain;
|
|
125
|
+
readonly failure_class: AgentSamErrorFailureClass;
|
|
126
|
+
readonly stage: AgentSamErrorStage;
|
|
127
|
+
readonly feature: string | null;
|
|
128
|
+
readonly failure_behavior: AgentSamErrorFailureBehavior | null;
|
|
129
|
+
readonly severity: AgentSamErrorSeverity;
|
|
130
|
+
readonly retryable: boolean;
|
|
131
|
+
readonly side_effect_state: AgentSamErrorSideEffectState;
|
|
132
|
+
readonly status: number | null;
|
|
133
|
+
readonly fingerprint: string;
|
|
134
|
+
}
|
|
135
|
+
export function asAgentSamError(value: unknown, options?: { cause?: unknown }): AgentSamError;
|
|
136
|
+
|
|
137
|
+
export function classifyFailure(source: string, evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
138
|
+
export function classifyProviderFailure(provider: string, evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
139
|
+
export function reasonFromGenericEvidence(evidence?: Record<string, unknown>): AgentSamErrorReason;
|
|
140
|
+
export function classifyOpenAIFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
141
|
+
export function classifyAnthropicFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
142
|
+
export function classifyGoogleAIFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
143
|
+
export function classifyGeminiFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
144
|
+
export function classifyCursorFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
145
|
+
export function classifyGitHubFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
146
|
+
export function classifyCloudflareFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
147
|
+
export function classifyDockerFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
148
|
+
export function classifyGcpFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
149
|
+
export function classifyProcessFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
150
|
+
export function classifyDeviceFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
151
|
+
export function classifyInternalFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
152
|
+
export function classifyFreeCadFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
153
|
+
export function classifyOpenScadFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
154
|
+
export function classifyBlenderFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
155
|
+
export function classifyMeshyFailure(evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
156
|
+
export function classifyOAuthFailure(error: unknown, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
157
|
+
export function classifyCadFailure(tool: string, evidence?: Record<string, unknown>, context?: Record<string, unknown>): Readonly<AgentSamErrorEnvelope>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inneranimalmedia/agentsam-goap",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.10",
|
|
4
4
|
"description": "Portable AgentSam GOAP control-plane contracts and adapters over existing blackboard, ticket, workflow, execution, approval, queue, runtime, and evidence stores.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"test": "node --test test/*.test.mjs"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@inneranimalmedia/agentsam-errors": "2.6.
|
|
23
|
+
"@inneranimalmedia/agentsam-errors": "2.6.10"
|
|
24
24
|
},
|
|
25
25
|
"engines": {
|
|
26
26
|
"node": ">=22 <25"
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# `@inneranimalmedia/agentsam-hooks`
|
|
2
|
+
|
|
3
|
+
Portable lifecycle policy and observability for AgentSam runtimes.
|
|
4
|
+
|
|
5
|
+
AgentSam Hooks is not tied to one model vendor, CLI, machine layout, or application account. The in-process JavaScript API and the JSON wire protocol share the same `agentsam.hook.v1` envelope. A hook can therefore be implemented in JavaScript/TypeScript, Python, Go, Rust, or any process or HTTP service that reads and writes JSON.
|
|
6
|
+
|
|
7
|
+
## What ships
|
|
8
|
+
|
|
9
|
+
- ordered callback runtime with explicit fail-open, fail-closed, and propagate-error behavior;
|
|
10
|
+
- pre/post model and tool adapters without provider wire-format assumptions;
|
|
11
|
+
- command and HTTP adapters for cross-language hooks;
|
|
12
|
+
- portable `agentsam.hooks.json` / `.agentsam/hooks.json` config discovery;
|
|
13
|
+
- bounded subprocess execution (timeout, output ceiling, no shell interpolation, minimal inherited environment);
|
|
14
|
+
- hook receipts that record timing and field names without persisting raw prompts, credentials, tool arguments, or model results;
|
|
15
|
+
- MCP, LSP, sub-agent, and composite capability adapters;
|
|
16
|
+
- TypeScript declarations plus Python, Go, and Rust protocol bindings;
|
|
17
|
+
- a small `agentsam-hooks` CLI for validation, inspection, and JSON invocation.
|
|
18
|
+
|
|
19
|
+
The package does **not** make hook output authoritative over AgentSam authorization, capability discovery, manifests, or verification receipts. Hooks may narrow or request permission. The trusted host still resolves identity and grants.
|
|
20
|
+
|
|
21
|
+
## JavaScript / TypeScript
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
import {
|
|
25
|
+
AgentSamHooks,
|
|
26
|
+
createHookedCapabilityAdapter,
|
|
27
|
+
createHookedProviderAdapter,
|
|
28
|
+
} from '@inneranimalmedia/agentsam-hooks';
|
|
29
|
+
|
|
30
|
+
const hooks = new AgentSamHooks({
|
|
31
|
+
hooks: {
|
|
32
|
+
pre_tool_use: {
|
|
33
|
+
id: 'workspace-policy',
|
|
34
|
+
failure_mode: 'closed',
|
|
35
|
+
handler: async ({ input }) => {
|
|
36
|
+
if (input.tool_name === 'terminal.exec' && input.tool_args?.dangerous === true) {
|
|
37
|
+
return {
|
|
38
|
+
permission_decision: 'ask',
|
|
39
|
+
permission_decision_reason: 'This command requires an explicit host approval.',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return { permission_decision: 'allow' };
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
post_tool_use: async ({ input }) => ({
|
|
46
|
+
modified_result: redactForModel(input.tool_result),
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const capabilityAdapter = createHookedCapabilityAdapter(tools, {
|
|
52
|
+
hookRuntime: hooks,
|
|
53
|
+
requestPermission: showApprovalDialog,
|
|
54
|
+
invocation: { session_id: session.id, run_id: run.id },
|
|
55
|
+
});
|
|
56
|
+
const provider = createHookedProviderAdapter(modelProvider, {
|
|
57
|
+
hookRuntime: hooks,
|
|
58
|
+
invocation: { session_id: session.id, run_id: run.id },
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Handlers receive one canonical envelope:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"schema": "agentsam.hook.v1",
|
|
67
|
+
"hook": "pre_tool_use",
|
|
68
|
+
"timestamp": 1790899200000,
|
|
69
|
+
"cwd": "/portable/project",
|
|
70
|
+
"invocation": { "session_id": "session-local", "run_id": "run-local" },
|
|
71
|
+
"input": {
|
|
72
|
+
"tool_name": "repository.snapshot",
|
|
73
|
+
"tool_args": { "include_untracked": false }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Canonical wire fields use `snake_case`. JavaScript hook outputs also accept the common camel-case spellings, but command/HTTP integrations should emit the canonical form.
|
|
79
|
+
|
|
80
|
+
## Cross-language command hook
|
|
81
|
+
|
|
82
|
+
`.agentsam/hooks.json`:
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"schema": "agentsam.hooks.config.v1",
|
|
87
|
+
"adapters": {
|
|
88
|
+
"project-policy": {
|
|
89
|
+
"type": "command",
|
|
90
|
+
"command": "python3",
|
|
91
|
+
"args": ["hooks/project_policy.py"],
|
|
92
|
+
"timeout_ms": 3000
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"hooks": {
|
|
96
|
+
"pre_tool_use": [
|
|
97
|
+
{
|
|
98
|
+
"id": "project-policy",
|
|
99
|
+
"adapter": "project-policy",
|
|
100
|
+
"failure_mode": "closed"
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The executable reads one invocation envelope from stdin and writes one hook output JSON object to stdout. Diagnostic logs belong on stderr. The last non-empty stdout line is parsed as the response.
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
import json, sys
|
|
111
|
+
|
|
112
|
+
envelope = json.load(sys.stdin)
|
|
113
|
+
tool = envelope["input"]["tool_name"]
|
|
114
|
+
if tool == "terminal.exec":
|
|
115
|
+
print(json.dumps({
|
|
116
|
+
"permission_decision": "ask",
|
|
117
|
+
"permission_decision_reason": "Terminal access needs approval"
|
|
118
|
+
}))
|
|
119
|
+
else:
|
|
120
|
+
print(json.dumps({"permission_decision": "allow"}))
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The command adapter does not invoke a shell. Relative command and cwd paths are resolved from the config directory, command cwd cannot escape that directory by default, output is bounded, and only portable process variables are inherited unless `inherit_environment` is explicitly enabled. Secrets can be referenced as `env:VARIABLE_NAME` in configured headers/environment values; values are resolved at runtime and do not belong in config.
|
|
124
|
+
|
|
125
|
+
## CLI
|
|
126
|
+
|
|
127
|
+
```sh
|
|
128
|
+
agentsam-hooks validate .agentsam/hooks.json
|
|
129
|
+
agentsam-hooks list .agentsam/hooks.json
|
|
130
|
+
printf '%s\n' '{"tool_name":"repository.snapshot","tool_args":{}}' \
|
|
131
|
+
| agentsam-hooks invoke pre_tool_use .agentsam/hooks.json
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`invoke` is useful for conformance tests and host runtimes. It is not a permission bypass: the host must still enforce the returned decision.
|
|
135
|
+
|
|
136
|
+
The full AgentSam CLI also exposes the merged code/config/stored view and its
|
|
137
|
+
execution evidence:
|
|
138
|
+
|
|
139
|
+
```sh
|
|
140
|
+
agentsam hooks status
|
|
141
|
+
agentsam hooks list
|
|
142
|
+
agentsam hooks executions
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Portable storage pack
|
|
146
|
+
|
|
147
|
+
`schema/manifest.json` describes the optional `agentsam.hooks` schema pack. Its
|
|
148
|
+
SQLite/D1-compatible migration creates only `agentsam_hook` and
|
|
149
|
+
`agentsam_hook_execution`. Portable ownership is an opaque `owner_id` plus an
|
|
150
|
+
optional `scope_type`/`scope_ref`; it does not require tenant, workspace, user,
|
|
151
|
+
workflow, or credential tables.
|
|
152
|
+
|
|
153
|
+
```js
|
|
154
|
+
import { createHookStore, createHookRuntime, registerStoredHooks } from '@inneranimalmedia/agentsam-hooks';
|
|
155
|
+
|
|
156
|
+
const store = createHookStore(db, { ownerId: accountId });
|
|
157
|
+
const runtime = createHookRuntime({ onReceipt: receipt => store.recordExecution(receipt) });
|
|
158
|
+
await registerStoredHooks(runtime, store, {
|
|
159
|
+
cwd: projectRoot,
|
|
160
|
+
context: { project_root: projectRoot, session_id: sessionId },
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Receipts retain ids, timing, input/output **keys**, status, and redacted errors;
|
|
165
|
+
they do not retain prompt, model response, tool argument, or tool result values.
|
|
166
|
+
Handler secrets must remain host-owned references. Config `match` and stored
|
|
167
|
+
`match_json` use the same portable partial-object matcher with exact values plus
|
|
168
|
+
`$eq`, `$in`, `$exists`, and `$contains`; unmatched hooks are not invoked and do
|
|
169
|
+
not emit execution receipts.
|
|
170
|
+
|
|
171
|
+
## Composition semantics
|
|
172
|
+
|
|
173
|
+
Hooks run by ascending `priority`, then ID. Modifications are passed into the next hook. Additional context is appended in order. Suppression is sticky. A `deny`, `ask`, or agent-stop `block` decision ends that hook chain.
|
|
174
|
+
|
|
175
|
+
Failure modes:
|
|
176
|
+
|
|
177
|
+
| Mode | Behavior |
|
|
178
|
+
| --- | --- |
|
|
179
|
+
| `open` | Record the failure and continue. Default for observational/post hooks. |
|
|
180
|
+
| `closed` | Record the failure and return a deny decision. Default for pre-tool and pre-model hooks. |
|
|
181
|
+
| `error` | Raise `HookExecutionError` to the host. |
|
|
182
|
+
|
|
183
|
+
Tool/model wrappers support bounded retries only when an `error_occurred` hook explicitly returns `error_handling: "retry"`. The host cap defaults to three. Hooks cannot create an unbounded retry loop.
|
|
184
|
+
|
|
185
|
+
## MCP, LSP, and sub-agents
|
|
186
|
+
|
|
187
|
+
The capability adapters depend on narrow host ports rather than a particular transport package:
|
|
188
|
+
|
|
189
|
+
- `createMcpCapabilityAdapter({ servers, listTools, callTool })`
|
|
190
|
+
- `createLspCapabilityAdapter({ languages, request })`
|
|
191
|
+
- `createAgentCapabilityAdapter({ agents, runAgent, hookRuntime })`
|
|
192
|
+
- `createCompositeCapabilityAdapter([...adapters])`
|
|
193
|
+
|
|
194
|
+
This lets an application use stdio MCP, remote MCP, an embedded language server, or a remote agent scheduler without changing the model loop. Discovery happens before capability schemas are presented to a model; authorization and execution remain host-owned.
|
|
195
|
+
|
|
196
|
+
## Protocol and bindings
|
|
197
|
+
|
|
198
|
+
JSON Schemas are owned once by the repository authority at `protocol/hooks/` and copied into the package artifact by its `prepack` gate. Bindings are included under `bindings/go` and `bindings/rust`; the Python package exposes `agentsam_sdk.hooks`. These bindings model the same envelope/output rather than introducing language-specific lifecycle semantics.
|
|
199
|
+
|
|
200
|
+
## Security defaults
|
|
201
|
+
|
|
202
|
+
- Do not place credential values in hook input, config, receipts, or logs.
|
|
203
|
+
- Treat argument/result modification as trusted-code behavior.
|
|
204
|
+
- Use `closed` for permission and policy hooks.
|
|
205
|
+
- Keep post hooks fast; move analytics persistence behind a queue if needed.
|
|
206
|
+
- Use explicit project config. This package does not silently load another user's home-directory plugins or machine-specific setup.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { createHookRuntimeFromConfig, findHookConfig, loadHookConfig } from '../src/index.js';
|
|
4
|
+
|
|
5
|
+
function usage() {
|
|
6
|
+
return `AgentSam Hooks\n\nUsage:\n agentsam-hooks validate [config]\n agentsam-hooks list [config]\n agentsam-hooks invoke <hook> [config]\n\n'invoke' reads one JSON object from stdin and writes one JSON dispatch result.\nConfig discovery checks agentsam.hooks.json and .agentsam/hooks.json from cwd upward.\n`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function resolveConfig(argument) {
|
|
10
|
+
const filename = argument || findHookConfig(process.cwd());
|
|
11
|
+
if (!filename) throw new Error('No AgentSam hook config found. Pass a config path or create .agentsam/hooks.json.');
|
|
12
|
+
return filename;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function readStdin() {
|
|
16
|
+
if (process.stdin.isTTY) return {};
|
|
17
|
+
const chunks = [];
|
|
18
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
19
|
+
const text = Buffer.concat(chunks).toString('utf8').trim();
|
|
20
|
+
return text ? JSON.parse(text) : {};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function main(argv) {
|
|
24
|
+
const [command, ...args] = argv;
|
|
25
|
+
if (!command || ['help', '--help', '-h'].includes(command)) {
|
|
26
|
+
process.stdout.write(usage());
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (command === 'validate') {
|
|
30
|
+
const filename = resolveConfig(args[0]);
|
|
31
|
+
const config = loadHookConfig(filename);
|
|
32
|
+
process.stdout.write(`${JSON.stringify({ ok: true, schema: config.schema, source: config.source, adapters: Object.keys(config.adapters).length, hooks: Object.values(config.hooks).reduce((sum, rows) => sum + rows.length, 0) }, null, 2)}\n`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (command === 'list') {
|
|
36
|
+
const filename = resolveConfig(args[0]);
|
|
37
|
+
const { runtime, config } = createHookRuntimeFromConfig(filename);
|
|
38
|
+
process.stdout.write(`${JSON.stringify({ schema: config.schema, source: config.source, hooks: runtime.list() }, null, 2)}\n`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (command === 'invoke') {
|
|
42
|
+
const hook = args[0];
|
|
43
|
+
if (!hook) throw new Error('invoke requires a hook event');
|
|
44
|
+
const filename = resolveConfig(args[1]);
|
|
45
|
+
fs.accessSync(filename, fs.constants.R_OK);
|
|
46
|
+
const { runtime } = createHookRuntimeFromConfig(filename);
|
|
47
|
+
const payload = await readStdin();
|
|
48
|
+
const isEnvelope = payload?.schema === 'agentsam.hook.v1';
|
|
49
|
+
const result = await runtime.dispatch(
|
|
50
|
+
hook,
|
|
51
|
+
isEnvelope ? payload.input || {} : payload,
|
|
52
|
+
isEnvelope ? payload.invocation || {} : {},
|
|
53
|
+
{ cwd: isEnvelope ? payload.cwd : process.cwd() },
|
|
54
|
+
);
|
|
55
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
throw new Error(`Unknown command '${command}'.\n\n${usage()}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
main(process.argv.slice(2)).catch((error) => {
|
|
62
|
+
process.stderr.write(`agentsam-hooks: ${error.message}\n`);
|
|
63
|
+
process.exitCode = 1;
|
|
64
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
package agentsamhooks
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"context"
|
|
6
|
+
"encoding/json"
|
|
7
|
+
"fmt"
|
|
8
|
+
"os"
|
|
9
|
+
"os/exec"
|
|
10
|
+
"strings"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
type CommandOptions struct {
|
|
14
|
+
Command string
|
|
15
|
+
Args []string
|
|
16
|
+
CWD string
|
|
17
|
+
Environment map[string]string
|
|
18
|
+
InheritEnvironment bool
|
|
19
|
+
MaxOutputBytes int
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
var portableEnvironmentKeys = []string{"PATH", "Path", "PATHEXT", "SYSTEMROOT", "WINDIR", "HOME", "USERPROFILE", "TMPDIR", "TMP", "TEMP", "LANG", "LC_ALL"}
|
|
23
|
+
|
|
24
|
+
func commandEnvironment(options CommandOptions) []string {
|
|
25
|
+
values := map[string]string{}
|
|
26
|
+
if options.InheritEnvironment {
|
|
27
|
+
for _, row := range os.Environ() { key, value, ok := strings.Cut(row, "="); if ok { values[key] = value } }
|
|
28
|
+
} else {
|
|
29
|
+
for _, key := range portableEnvironmentKeys { if value, ok := os.LookupEnv(key); ok { values[key] = value } }
|
|
30
|
+
}
|
|
31
|
+
for key, value := range options.Environment { values[key] = value }
|
|
32
|
+
output := make([]string, 0, len(values)); for key, value := range values { output = append(output, key+"="+value) }; return output
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// CommandHandler starts one process per invocation. It never invokes a shell.
|
|
36
|
+
// The process reads one envelope from stdin and writes one output JSON object.
|
|
37
|
+
func CommandHandler(options CommandOptions) Handler {
|
|
38
|
+
return func(ctx context.Context, envelope Envelope) (*Output, error) {
|
|
39
|
+
if strings.TrimSpace(options.Command) == "" { return nil, fmt.Errorf("hook_command_required") }
|
|
40
|
+
limit := options.MaxOutputBytes; if limit <= 0 { limit = 1_048_576 }
|
|
41
|
+
payload, err := json.Marshal(envelope); if err != nil { return nil, err }
|
|
42
|
+
command := exec.CommandContext(ctx, options.Command, options.Args...)
|
|
43
|
+
command.Dir = options.CWD; if command.Dir == "" { command.Dir = envelope.CWD }
|
|
44
|
+
command.Env = commandEnvironment(options); command.Stdin = bytes.NewReader(append(payload, '\n'))
|
|
45
|
+
var stdout, stderr bytes.Buffer; command.Stdout = &stdout; command.Stderr = &stderr
|
|
46
|
+
if err := command.Run(); err != nil { return nil, fmt.Errorf("hook_command_failed:%s:%w:%s", options.Command, err, stderr.String()) }
|
|
47
|
+
if stdout.Len()+stderr.Len() > limit { return nil, fmt.Errorf("hook_command_output_limit_exceeded:%d", limit) }
|
|
48
|
+
lines := strings.Split(strings.TrimSpace(stdout.String()), "\n"); if len(lines) == 0 || strings.TrimSpace(lines[len(lines)-1]) == "" { return nil, nil }
|
|
49
|
+
output := &Output{}
|
|
50
|
+
decoder := json.NewDecoder(strings.NewReader(lines[len(lines)-1]))
|
|
51
|
+
decoder.DisallowUnknownFields()
|
|
52
|
+
if err := decoder.Decode(output); err != nil { return nil, fmt.Errorf("hook_command_invalid_json:%w", err) }
|
|
53
|
+
return output, nil
|
|
54
|
+
}
|
|
55
|
+
}
|