@lazyingart/agintiflow 0.20.331 → 0.20.336-integration.2
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 +2 -0
- package/bin/aginti-document-worker-runtime-check.js +12 -0
- package/bin/aginti-document-worker.js +10 -0
- package/bin/aginti-execution-worker.js +26 -0
- package/bin/aginti-integration-analysis-state.js +10 -0
- package/bin/aginti-integration-analysis.js +7 -0
- package/bin/aginti-public-research.js +1 -0
- package/bin/aginti-worker-directory.js +178 -0
- package/docs/integration-analysis-state-migration.md +47 -0
- package/docs/integration-deep-research.md +9 -0
- package/docs/integration-worker-directory.md +99 -0
- package/docs/labcanvas-chatops-fallback.md +139 -0
- package/docs/supervision-campaign-ledger.md +1851 -0
- package/examples/document-worker/README.md +84 -0
- package/examples/document-worker/aginti-document-worker.service +67 -0
- package/examples/document-worker/document-worker.json +12 -0
- package/examples/document-worker/runtime-manifest.json +28 -0
- package/package.json +89 -4
- package/scripts/fixtures/integration-document-worker-smoke-fixture.js +190 -0
- package/scripts/fixtures/loopback-test-port.mjs +16 -0
- package/scripts/github-hosted-release-environment.js +22 -0
- package/scripts/live-production-planner-smoke.js +70 -0
- package/scripts/smoke-canvas-artifacts.js +203 -0
- package/scripts/smoke-coding-tools.js +676 -1
- package/scripts/smoke-docker-command.js +24 -0
- package/scripts/smoke-dynamic-step-budget.js +1032 -27
- package/scripts/smoke-execution-runtime-bundle.js +248 -0
- package/scripts/smoke-execution-worker-server.js +138 -0
- package/scripts/smoke-execution-worker-systemd-boundary.js +545 -0
- package/scripts/smoke-execution-worker.js +791 -0
- package/scripts/smoke-integration-analysis-api-server.js +1038 -0
- package/scripts/smoke-integration-analysis-coordinator.js +1035 -0
- package/scripts/smoke-integration-analysis-planner.js +5366 -0
- package/scripts/smoke-integration-analysis-session-authority-contract.js +223 -0
- package/scripts/smoke-integration-analysis-session-service.js +4628 -0
- package/scripts/smoke-integration-analysis-startup-recovery.js +846 -0
- package/scripts/smoke-integration-analysis-state-migrator.js +765 -0
- package/scripts/smoke-integration-analysis-state-prewrite-gate.js +1170 -0
- package/scripts/smoke-integration-api.js +2275 -0
- package/scripts/smoke-integration-authorities.js +3338 -0
- package/scripts/smoke-integration-document-artifacts.js +431 -0
- package/scripts/smoke-integration-document-content.js +244 -0
- package/scripts/smoke-integration-document-revision.js +909 -0
- package/scripts/smoke-integration-document-session-broker.js +1374 -0
- package/scripts/smoke-integration-document-worker-contract.js +197 -0
- package/scripts/smoke-integration-document-worker-cross-boundary.js +209 -0
- package/scripts/smoke-integration-document-worker-longevity.js +257 -0
- package/scripts/smoke-integration-document-worker-real-compile.js +138 -0
- package/scripts/smoke-integration-document-worker-server.js +411 -0
- package/scripts/smoke-integration-document-worker-service.js +214 -0
- package/scripts/smoke-integration-document-worker-store.js +419 -0
- package/scripts/smoke-integration-document-worker-systemd-boundary.js +353 -0
- package/scripts/smoke-integration-execution-worker-binding-config.js +102 -0
- package/scripts/smoke-integration-grounded-search.js +1355 -0
- package/scripts/smoke-integration-sandbox-profile.js +982 -0
- package/scripts/smoke-integration-worker-directory-cli.js +37 -0
- package/scripts/smoke-integration-worker-directory.js +399 -0
- package/scripts/smoke-local-failure-recovery.js +28 -0
- package/scripts/smoke-math-rendering.js +2 -1
- package/scripts/smoke-model-roles.js +46 -0
- package/scripts/smoke-public-research-wrapper.js +11 -3
- package/scripts/smoke-runtime-compat.js +8 -1
- package/scripts/smoke-scs-evidence-visibility.js +65 -0
- package/scripts/smoke-session-runtime.js +28 -0
- package/scripts/smoke-skills.js +174 -0
- package/scripts/smoke-tmux-tools.js +26 -0
- package/scripts/smoke-toolchain-docker.js +18 -0
- package/scripts/smoke-truthful-completion.js +3547 -117
- package/scripts/smoke-web-api.js +11 -2
- package/scripts/smoke-web-ui.js +11 -2
- package/scripts/smoke-webapp-command.js +67 -25
- package/scripts/test-document-worker-fixture.js +426 -0
- package/skills/deep-research/SKILL.md +1 -0
- package/src/agent-runner.js +3552 -174
- package/src/config.js +11 -5
- package/src/docker-sandbox.js +51 -9
- package/src/execution-runtime-bundle.js +639 -0
- package/src/execution-worker-api.js +245 -0
- package/src/execution-worker-client.js +1017 -0
- package/src/execution-worker-jobs.js +399 -0
- package/src/execution-worker-server.js +239 -0
- package/src/execution-worker-systemd-boundary.js +654 -0
- package/src/execution-worker.js +1453 -0
- package/src/guardrails.js +8 -1
- package/src/integration-analysis-cli.js +255 -0
- package/src/integration-analysis-config.js +614 -0
- package/src/integration-analysis-coordinator.js +529 -0
- package/src/integration-analysis-planner.js +4647 -0
- package/src/integration-analysis-server.js +778 -0
- package/src/integration-analysis-session-contract.js +18 -0
- package/src/integration-analysis-session-service.js +8269 -0
- package/src/integration-analysis-state-migration-cli.js +366 -0
- package/src/integration-analysis-state-migrator.js +591 -0
- package/src/integration-analysis-state-persistence.js +17 -0
- package/src/integration-analysis-vision.js +1086 -0
- package/src/integration-api.js +1505 -38
- package/src/integration-artifacts.js +235 -7
- package/src/integration-auth.js +1 -1
- package/src/integration-core-event-projector.js +10 -0
- package/src/integration-document-artifacts.js +492 -0
- package/src/integration-document-blob-store.js +226 -0
- package/src/integration-document-worker-cli.js +186 -0
- package/src/integration-document-worker-client.js +1719 -0
- package/src/integration-document-worker-config.js +299 -0
- package/src/integration-document-worker-contract.js +790 -0
- package/src/integration-document-worker-requirements.js +119 -0
- package/src/integration-document-worker-runtime.js +199 -0
- package/src/integration-document-worker-server.js +565 -0
- package/src/integration-document-worker-service.js +448 -0
- package/src/integration-document-worker-store.js +1837 -0
- package/src/integration-durable-common.js +586 -68
- package/src/integration-events.js +12 -2
- package/src/integration-execution-worker-binding-config.js +223 -0
- package/src/integration-execution-worker-router.js +428 -0
- package/src/integration-explicit-python.js +247 -0
- package/src/integration-expression-plot.js +361 -0
- package/src/integration-file-worker-client.js +725 -0
- package/src/integration-file-worker-contract.js +559 -0
- package/src/integration-file-worker-store.js +1100 -0
- package/src/integration-grounded-search.js +2612 -0
- package/src/integration-idempotency-store.js +429 -10
- package/src/integration-policy.js +242 -8
- package/src/integration-tex-compiler.js +654 -0
- package/src/integration-worker-directory.js +1015 -0
- package/src/json-extraction.js +66 -0
- package/src/model-client.js +293 -2
- package/src/perception-tools.js +3 -63
- package/src/permission-advice.js +152 -3
- package/src/public-research-server.js +1 -1
- package/src/response-only-source-quality.js +356 -0
- package/src/scs-controller.js +37 -1
- package/src/scs-evidence.js +516 -32
- package/src/session-runtime.js +8 -0
- package/src/skill-library.js +81 -5
- package/src/step-budget-controller.js +175 -18
- package/src/tmux-tools.js +10 -1
- package/src/web-query-privacy.js +44 -0
- package/src/web-search.js +11 -0
- package/src/workspace-tools.js +92 -1
package/README.md
CHANGED
|
@@ -409,6 +409,7 @@ More detail:
|
|
|
409
409
|
- [docs/auxiliary-image-generation.md](docs/auxiliary-image-generation.md)
|
|
410
410
|
- [docs/perception-and-web-research.md](docs/perception-and-web-research.md)
|
|
411
411
|
- [docs/deep-research-engine.md](docs/deep-research-engine.md)
|
|
412
|
+
- [docs/integration-deep-research.md](docs/integration-deep-research.md)
|
|
412
413
|
- [docs/cli-i18n.md](docs/cli-i18n.md)
|
|
413
414
|
- [docs/skillmesh.md](docs/skillmesh.md)
|
|
414
415
|
|
|
@@ -420,6 +421,7 @@ More detail:
|
|
|
420
421
|
| AgentLink | [docs/agentlink.md](docs/agentlink.md) |
|
|
421
422
|
| Model selection and roles | [docs/model-selection.md](docs/model-selection.md) |
|
|
422
423
|
| Local-first provider and agent boundary | [docs/local-first-agent-runtime.md](docs/local-first-agent-runtime.md) |
|
|
424
|
+
| Replaceable integration worker coordination | [docs/integration-worker-directory.md](docs/integration-worker-directory.md) |
|
|
423
425
|
| SCS mode | [docs/student-committee-supervisor.md](docs/student-committee-supervisor.md) |
|
|
424
426
|
| Large-codebase engineering | [docs/large-codebase-engineering.md](docs/large-codebase-engineering.md) |
|
|
425
427
|
| Runtime modes and autonomy | [docs/runtime-modes-and-autonomy.md](docs/runtime-modes-and-autonomy.md) |
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { verifyIntegrationDocumentWorkerNodeRuntime } from "../src/integration-document-worker-runtime.js";
|
|
3
|
+
|
|
4
|
+
if (process.argv.length !== 2) {
|
|
5
|
+
process.stderr.write("aginti-document-worker-runtime-check: RUNTIME_IDENTITY_INVALID\n");
|
|
6
|
+
process.exitCode = 1;
|
|
7
|
+
} else {
|
|
8
|
+
verifyIntegrationDocumentWorkerNodeRuntime().catch(() => {
|
|
9
|
+
process.stderr.write("aginti-document-worker-runtime-check: RUNTIME_IDENTITY_INVALID\n");
|
|
10
|
+
process.exitCode = 1;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
main,
|
|
4
|
+
safeIntegrationDocumentWorkerCliError,
|
|
5
|
+
} from "../src/integration-document-worker-cli.js";
|
|
6
|
+
|
|
7
|
+
main().catch((error) => {
|
|
8
|
+
process.stderr.write(`aginti-document-worker: ${safeIntegrationDocumentWorkerCliError(error)}\n`);
|
|
9
|
+
process.exitCode = 1;
|
|
10
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
createProductionExecutionWorkerServer,
|
|
4
|
+
installExecutionWorkerShutdownHandlers,
|
|
5
|
+
loadExecutionWorkerServerConfig,
|
|
6
|
+
} from "../src/execution-worker-server.js";
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const config = await loadExecutionWorkerServerConfig();
|
|
10
|
+
const runtime = await createProductionExecutionWorkerServer({ config });
|
|
11
|
+
installExecutionWorkerShutdownHandlers(runtime.server);
|
|
12
|
+
console.log(JSON.stringify({
|
|
13
|
+
ok: true,
|
|
14
|
+
service: "aginti-execution-worker",
|
|
15
|
+
workerId: config.workerId,
|
|
16
|
+
runtimeBundleRootDigest: config.runtimeBundleRootDigest,
|
|
17
|
+
listener: runtime.address,
|
|
18
|
+
}));
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error(JSON.stringify({
|
|
21
|
+
ok: false,
|
|
22
|
+
service: "aginti-execution-worker",
|
|
23
|
+
code: typeof error?.code === "string" ? error.code : "EXECUTION_WORKER_START_FAILED",
|
|
24
|
+
}));
|
|
25
|
+
process.exitCode = 1;
|
|
26
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
main,
|
|
4
|
+
safeIntegrationAnalysisStateMigrationCliError,
|
|
5
|
+
} from "../src/integration-analysis-state-migration-cli.js";
|
|
6
|
+
|
|
7
|
+
main().catch((error) => {
|
|
8
|
+
process.stderr.write(`aginti-integration-analysis-state: ${safeIntegrationAnalysisStateMigrationCliError(error)}\n`);
|
|
9
|
+
process.exitCode = 1;
|
|
10
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { main, safeIntegrationAnalysisCliError } from "../src/integration-analysis-cli.js";
|
|
3
|
+
|
|
4
|
+
main().catch((error) => {
|
|
5
|
+
process.stderr.write(`aginti-integration-analysis: ${safeIntegrationAnalysisCliError(error)}\n`);
|
|
6
|
+
process.exitCode = 1;
|
|
7
|
+
});
|
|
@@ -9,6 +9,7 @@ function optionValue(argv, name) {
|
|
|
9
9
|
const argv = process.argv.slice(2);
|
|
10
10
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
11
11
|
console.log("Usage: aginti-public-research [--host 127.0.0.1] [--port 3211]");
|
|
12
|
+
console.log("Use --port 0 to let the operating system allocate an ephemeral loopback port.");
|
|
12
13
|
console.log("Starts only the fail-closed public research API; it does not start AgInTiFlow Studio or chat.");
|
|
13
14
|
process.exit(0);
|
|
14
15
|
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
INTEGRATION_WORKER_DIRECTORY_PLATFORMS,
|
|
5
|
+
INTEGRATION_WORKER_DIRECTORY_ROLES,
|
|
6
|
+
createIntegrationWorkerDirectory,
|
|
7
|
+
} from "../src/integration-worker-directory.js";
|
|
8
|
+
import { createSystemdExecutionWorkerBindingAuthority } from "../src/integration-execution-worker-router.js";
|
|
9
|
+
|
|
10
|
+
const HELP = `Usage: aginti-worker-directory <command> [options]
|
|
11
|
+
|
|
12
|
+
Read commands:
|
|
13
|
+
status
|
|
14
|
+
events
|
|
15
|
+
resolve --role <role>
|
|
16
|
+
|
|
17
|
+
Mutation commands:
|
|
18
|
+
enroll --node-id <id> --binding-id <id> --platform <platform> --roles <role,...>
|
|
19
|
+
renew --node-id <id>
|
|
20
|
+
switch --role <role> --node-id <id> --expected-generation <n>
|
|
21
|
+
rollback --role <role> --expected-generation <n>
|
|
22
|
+
finalize --role <role> --expected-generation <n>
|
|
23
|
+
retire --node-id <id>
|
|
24
|
+
remove --node-id <id>
|
|
25
|
+
|
|
26
|
+
The command reads only the fixed owner-only manifest and systemd credentials.
|
|
27
|
+
It never accepts an endpoint, host, port, URL, token, or credential name.
|
|
28
|
+
|
|
29
|
+
Roles: ${INTEGRATION_WORKER_DIRECTORY_ROLES.join(", ")}
|
|
30
|
+
Platforms: ${INTEGRATION_WORKER_DIRECTORY_PLATFORMS.join(", ")}
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const COMMAND_FLAGS = Object.freeze({
|
|
34
|
+
status: Object.freeze([]),
|
|
35
|
+
events: Object.freeze([]),
|
|
36
|
+
resolve: Object.freeze(["role"]),
|
|
37
|
+
enroll: Object.freeze(["node-id", "binding-id", "platform", "roles"]),
|
|
38
|
+
renew: Object.freeze(["node-id"]),
|
|
39
|
+
switch: Object.freeze(["role", "node-id", "expected-generation"]),
|
|
40
|
+
rollback: Object.freeze(["role", "expected-generation"]),
|
|
41
|
+
finalize: Object.freeze(["role", "expected-generation"]),
|
|
42
|
+
retire: Object.freeze(["node-id"]),
|
|
43
|
+
remove: Object.freeze(["node-id"]),
|
|
44
|
+
});
|
|
45
|
+
const PROBED_COMMANDS = new Set(["enroll", "renew", "switch", "rollback"]);
|
|
46
|
+
|
|
47
|
+
function usage(message) {
|
|
48
|
+
const error = new Error(message);
|
|
49
|
+
error.code = "WORKER_DIRECTORY_CLI_USAGE";
|
|
50
|
+
error.status = 400;
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function parse(argv) {
|
|
55
|
+
if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h" || argv[0] === "help") {
|
|
56
|
+
return Object.freeze({ command: "help", options: Object.freeze({}) });
|
|
57
|
+
}
|
|
58
|
+
const command = argv[0];
|
|
59
|
+
const allowed = COMMAND_FLAGS[command];
|
|
60
|
+
if (!allowed) usage(`Unknown command: ${command}`);
|
|
61
|
+
const options = {};
|
|
62
|
+
for (let index = 1; index < argv.length; index += 2) {
|
|
63
|
+
const flag = argv[index];
|
|
64
|
+
const value = argv[index + 1];
|
|
65
|
+
if (typeof flag !== "string" || !flag.startsWith("--") || value === undefined) {
|
|
66
|
+
usage("Options must use --name value pairs.");
|
|
67
|
+
}
|
|
68
|
+
const name = flag.slice(2);
|
|
69
|
+
if (!allowed.includes(name)) usage(`Unsupported option for ${command}: --${name}`);
|
|
70
|
+
if (Object.prototype.hasOwnProperty.call(options, name)) usage(`Duplicate option: --${name}`);
|
|
71
|
+
options[name] = value;
|
|
72
|
+
}
|
|
73
|
+
for (const name of allowed) {
|
|
74
|
+
if (!Object.prototype.hasOwnProperty.call(options, name)) usage(`Missing option: --${name}`);
|
|
75
|
+
}
|
|
76
|
+
if (Object.prototype.hasOwnProperty.call(options, "expected-generation")) {
|
|
77
|
+
generation(options["expected-generation"]);
|
|
78
|
+
}
|
|
79
|
+
return Object.freeze({ command, options: Object.freeze(options) });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function generation(value) {
|
|
83
|
+
if (!/^(?:0|[1-9][0-9]{0,8})$/u.test(String(value || ""))) {
|
|
84
|
+
usage("--expected-generation must be a non-negative integer.");
|
|
85
|
+
}
|
|
86
|
+
return Number(value);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function roles(value) {
|
|
90
|
+
const result = String(value || "").split(",").filter(Boolean);
|
|
91
|
+
if (!result.length || result.some((role) => !INTEGRATION_WORKER_DIRECTORY_ROLES.includes(role))) {
|
|
92
|
+
usage("--roles contains an unsupported role.");
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function openDirectory(command) {
|
|
98
|
+
if (!PROBED_COMMANDS.has(command)) {
|
|
99
|
+
return Object.freeze({
|
|
100
|
+
directory: await createIntegrationWorkerDirectory({
|
|
101
|
+
probe: async () => {
|
|
102
|
+
throw new Error("worker probe is unavailable for this read-only command");
|
|
103
|
+
},
|
|
104
|
+
}),
|
|
105
|
+
close() {},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const authority = await createSystemdExecutionWorkerBindingAuthority();
|
|
109
|
+
try {
|
|
110
|
+
const directory = await createIntegrationWorkerDirectory({
|
|
111
|
+
probe: (candidate) => authority.probe(candidate),
|
|
112
|
+
});
|
|
113
|
+
return Object.freeze({ directory, close: () => authority.close() });
|
|
114
|
+
} catch (error) {
|
|
115
|
+
authority.close();
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function dispatch(parsed) {
|
|
121
|
+
if (parsed.command === "help") return HELP;
|
|
122
|
+
const opened = await openDirectory(parsed.command);
|
|
123
|
+
const { directory } = opened;
|
|
124
|
+
const options = parsed.options;
|
|
125
|
+
try {
|
|
126
|
+
switch (parsed.command) {
|
|
127
|
+
case "status":
|
|
128
|
+
return directory.status();
|
|
129
|
+
case "events":
|
|
130
|
+
return directory.events();
|
|
131
|
+
case "resolve":
|
|
132
|
+
return directory.resolve(options.role);
|
|
133
|
+
case "enroll":
|
|
134
|
+
return directory.enroll({
|
|
135
|
+
nodeId: options["node-id"],
|
|
136
|
+
bindingId: options["binding-id"],
|
|
137
|
+
platform: options.platform,
|
|
138
|
+
roles: roles(options.roles),
|
|
139
|
+
});
|
|
140
|
+
case "renew":
|
|
141
|
+
return directory.renew(options["node-id"]);
|
|
142
|
+
case "switch":
|
|
143
|
+
return directory.switchRole(options.role, options["node-id"], {
|
|
144
|
+
expectedGeneration: generation(options["expected-generation"]),
|
|
145
|
+
});
|
|
146
|
+
case "rollback":
|
|
147
|
+
return directory.rollbackRole(options.role, {
|
|
148
|
+
expectedGeneration: generation(options["expected-generation"]),
|
|
149
|
+
});
|
|
150
|
+
case "finalize":
|
|
151
|
+
return directory.finalizeRole(options.role, {
|
|
152
|
+
expectedGeneration: generation(options["expected-generation"]),
|
|
153
|
+
});
|
|
154
|
+
case "retire":
|
|
155
|
+
return directory.retire(options["node-id"]);
|
|
156
|
+
case "remove":
|
|
157
|
+
return directory.remove(options["node-id"]);
|
|
158
|
+
default:
|
|
159
|
+
usage("Unsupported command.");
|
|
160
|
+
}
|
|
161
|
+
} finally {
|
|
162
|
+
opened.close();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const parsed = parse(process.argv.slice(2));
|
|
168
|
+
const result = await dispatch(parsed);
|
|
169
|
+
process.stdout.write(typeof result === "string" ? result : `${JSON.stringify(result, null, 2)}\n`);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
process.stderr.write(`${JSON.stringify({
|
|
172
|
+
error: {
|
|
173
|
+
code: error?.code || "WORKER_DIRECTORY_CLI_FAILED",
|
|
174
|
+
message: error?.message || "Worker directory command failed.",
|
|
175
|
+
},
|
|
176
|
+
})}\n`);
|
|
177
|
+
process.exitCode = 1;
|
|
178
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Integration analysis state migration
|
|
2
|
+
|
|
3
|
+
`aginti-integration-analysis-state` performs the forward-only storage-floor migration from canonical analysis state v2 to v3. It always processes every scope in one state root and does not accept a scope selector.
|
|
4
|
+
|
|
5
|
+
Stop the analysis service, then run the command as the operating-system user that owns the state root:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
aginti-integration-analysis-state migrate \
|
|
9
|
+
--offline \
|
|
10
|
+
--state-root /absolute/owner-only/analysis-state
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The command requires `--offline`. Its migration contract denies network access and does not load service configuration, credentials, model clients, search clients, or document-worker clients. Run it inside the same host network sandbox used for offline maintenance when the host provides one.
|
|
14
|
+
|
|
15
|
+
The migrator fails closed unless the root, `scopes` directory, every scope directory, and every `state.json` file have their expected canonical paths, current-user ownership, owner-only modes, and safe link counts. It takes the analysis-session ownership lock, performs a read-only validation of every scope before changing anything, accepts only canonical v2 or already-valid canonical v3 records, and refuses the entire migration if any persisted run is not terminal. A recognized crash-left temporary file is validated during this read-only pass and removed only after the whole root passes preflight.
|
|
16
|
+
|
|
17
|
+
A live analysis service or migrator always blocks the command. A lock left by a killed process becomes reclaimable only after 60 seconds and only when its schema, cryptographic owner token, PID, boot ID, process start identity, and acquisition timestamp are valid and that exact process identity is no longer live. This permits bounded crash recovery without breaking a live owner.
|
|
18
|
+
|
|
19
|
+
Each v2 record receives empty document-intent arrays and run lineage inferred by the runtime's own parser. The state revision and all existing semantics remain unchanged. The runtime's canonical encoder recomputes the envelope digest. Each converted scope is written to an owner-only temporary file, fsynced, atomically renamed, verified, and followed by a directory fsync.
|
|
20
|
+
|
|
21
|
+
Conversion is resumable without a rollback journal: completed scopes are valid v3, untouched scopes remain valid v2, and a rerun accepts that mixed state and continues forward. Scope directories are processed by explicit lowercase-hex code-unit order, independent of host locale. A second run over an all-v3 root does not rewrite files.
|
|
22
|
+
|
|
23
|
+
Successful output contains only counts, fixed contract fields, and aggregate SHA-256-derived digests. It never prints root paths, scope identifiers, principals, browser sessions, thread/run identifiers, prompts, messages, artifacts, or document content.
|
|
24
|
+
|
|
25
|
+
## Lock-held prewrite handshake
|
|
26
|
+
|
|
27
|
+
An operator that must complete an external durable cutover step between validation and the first state mutation can enable the prewrite gate:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
aginti-integration-analysis-state migrate \
|
|
31
|
+
--offline \
|
|
32
|
+
--state-root /absolute/owner-only/analysis-state \
|
|
33
|
+
--prewrite-gate-nonce "$cutover_nonce" \
|
|
34
|
+
--prewrite-gate-timeout-ms 120000
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Both gate flags are required together. The nonce must be exactly 64 lowercase hexadecimal characters. The timeout must be an integer from 1 through 600000 milliseconds. Generate a fresh unpredictable nonce in the controller for each attempt; do not reuse a nonce across independent cutovers.
|
|
38
|
+
|
|
39
|
+
The command takes `.analysis-session-owner.lock`, validates the complete root, computes every canonical v3 target, and writes one JSON gate record to stdout. It continues holding that same lock while it waits on stdin. The gate record is deterministic for a given nonce, timeout, current state, and target plan. It contains only fixed protocol fields, counts, aggregate digests, and a derived storage-state label; it never exposes a scope identity or path.
|
|
40
|
+
|
|
41
|
+
`sourceV2ScopeCount` and `sourceV3ScopeCount` always sum to `scopeCount`. `migrationTemporaryCount` counts validated crash-left migration temporary files and cannot exceed `scopeCount`. `currentStorageState` is derived from the source counts: `empty` for zero scopes, `all-v2` when every nonempty scope is v2, `all-v3` when every nonempty scope is v3, and `mixed` only when both source versions are present. A controller can therefore apply its marker/storage matrix without releasing the lock. The `targetAggregateDigest` is computed solely from each scope's canonical v3 output, so it remains identical for equivalent all-v2, partially migrated, and all-v3 roots even though their current-state metadata differs.
|
|
42
|
+
|
|
43
|
+
The controller must first durably complete and verify its external cutover step. It then writes the gate record's `requiredAck` value followed by exactly one LF byte to the migrator's stdin and closes its stdin write side. The migrator accepts the acknowledgement only after observing EOF, so the complete input must be exactly `requiredAck + "\n"` with no trailing byte. The single configured timeout covers receipt of both those bytes and EOF. The acknowledgement is bound to the nonce, target aggregate, and migration contract. Do not reconstruct or normalize it when the exact emitted value can be relayed.
|
|
44
|
+
|
|
45
|
+
Until that exact acknowledgement is received, the migrator does not remove a crash-left migration temporary file and does not create, replace, or rewrite any state or migration-temporary file. EOF, a mismatched acknowledgement, input failure, or timeout fails closed, releases the ownership lock, and leaves the preflight state untouched. After acknowledgement, the normal resumable migration runs under the continuously held lock. A second JSON line is written only after final verification and is the ordinary migration result.
|
|
46
|
+
|
|
47
|
+
The gate is a synchronization boundary, not remote authentication: stdout reveals the required acknowledgement by design. The controller is responsible for withholding it until its separately authenticated durable action is complete. Keep the analysis service stopped for the whole command, including the acknowledgement wait.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Integrated deep research
|
|
2
|
+
|
|
3
|
+
The durable analysis API promotes only explicit deep-research instructions such as “perform deep research” or “全面研究”. Ordinary questions and ordinary Search requests stay on their existing fast paths.
|
|
4
|
+
|
|
5
|
+
An eligible run uses the private LazyEdge-bound LocalLLM task protocol at the exact `create`, `status`, and `cancel` routes. AgInTi fixes the model alias, limits the depth to `quick`, `standard`, or `deep`, polls one stable task identity, cancels incomplete work on abort, and accepts only bounded no-cache responses. Exact domain, DOI, arXiv, and image-grounded requests retain the stricter one-shot Search path instead of weakening their constraints.
|
|
6
|
+
|
|
7
|
+
The completed report is accepted only with at least one safe source and valid one-based citations. Every source is preserved in one authority-bound `sources` artifact. A pure research request returns that validated report directly; a combined document, file, or execution request carries it forward as untrusted evidence for the remaining agent work.
|
|
8
|
+
|
|
9
|
+
Search/research intent, task activity, artifacts, failures, and terminal state use the same durable session ledger as other Agent work. The next message in the thread does not inherit stale research authority or completed activity. Public capabilities advertise the optional `localllm/research-task/v2` protocol while retaining the original Search contract for older Web clients.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Integration worker directory
|
|
2
|
+
|
|
3
|
+
Status: the durable directory, leased execution router, fixed-manifest binding authority, and production preference path are implemented and smoke-tested. Production remains on the fixed client until a manifest and initial assignment are installed and accepted.
|
|
4
|
+
|
|
5
|
+
AgInTi owns durable worker identity, role assignment, migration, rollback, drain, retirement, and removal. LazyEdge owns only authenticated transport and admission checks. LocalLLM and other worker services own their role-specific model or tool execution. Callers cannot choose a URL or credential.
|
|
6
|
+
|
|
7
|
+
## Stored state
|
|
8
|
+
|
|
9
|
+
The directory stores only:
|
|
10
|
+
|
|
11
|
+
- a stable node ID and opaque transport binding ID;
|
|
12
|
+
- platform and declared roles;
|
|
13
|
+
- short-lived admission evidence derived from readiness and capability probes;
|
|
14
|
+
- generation-checked role assignments;
|
|
15
|
+
- bounded execution leases used to drain an old worker safely;
|
|
16
|
+
- an integrity-sealed, hash-chained transition ledger.
|
|
17
|
+
|
|
18
|
+
It does not store endpoints, bearer tokens, cookies, passkeys, or other credentials. The default owner-only state root is `/var/lib/agintiflow-integration/worker-directory`; tests and isolated deployments can supply another absolute root.
|
|
19
|
+
|
|
20
|
+
## Admission contract
|
|
21
|
+
|
|
22
|
+
Enrollment and every switch or rollback require fresh probe evidence. Lease acquisition for the assigned node also refreshes evidence under the directory mutation lock when the stored admission is expired or cannot cover the requested lease TTL; sufficiently fresh admissions are reused without probing. Evidence binds the node and opaque binding, role set, transport kind, release and capability digests, canary digest, supported protocols, observation time, and expiry. Admission lifetime is at most ten minutes.
|
|
23
|
+
|
|
24
|
+
The probe belongs to the trusted coordinator bootstrap. Candidate input containing an endpoint or credential is rejected before any probe or write. A later transport-binding adapter may resolve an opaque binding to a systemd credential and LazyEdge route, but that adapter must remain outside caller input.
|
|
25
|
+
|
|
26
|
+
## Migration lifecycle
|
|
27
|
+
|
|
28
|
+
1. Enroll and probe a candidate.
|
|
29
|
+
2. Assign a role with the expected current generation.
|
|
30
|
+
3. Acquire bounded leases for work accepted under that generation.
|
|
31
|
+
4. Switch the role to a freshly probed replacement. New leases resolve to the replacement immediately; old leases remain attached to the previous node.
|
|
32
|
+
5. Roll back while the previous-node pointer is retained, or finalize only after its leases drain.
|
|
33
|
+
6. Retire and remove an unassigned, unleased node.
|
|
34
|
+
|
|
35
|
+
Every mutation is serialized by an owner-only directory lock and committed with an atomic protected JSON replacement. Corrupt integrity seals, event chains, symlinks, hardlinks, malformed records, stale evidence, and generation conflicts fail closed.
|
|
36
|
+
|
|
37
|
+
## Leased execution routing
|
|
38
|
+
|
|
39
|
+
The analysis coordinator now has a routed mode. It acquires one execution lease before capability validation, refreshing the assigned node admission first if needed, resolves that lease to the opaque binding, opens the binding through an AgInTi-owned authority, and renews the lease heartbeat through job start, polling, cancellation, terminal-ledger validation, artifact retrieval, and artifact callbacks. Lease renewal refreshes admission for the leased node when the current evidence cannot cover the next lease TTL, even after the role has switched to a replacement. A role switch affects only later leases, so an in-flight job never changes workers halfway through its protocol.
|
|
40
|
+
|
|
41
|
+
The router revalidates the worker capability digest against the admitted node evidence before starting a job. It releases the lease on success, cancellation, or failure only after the routed operation has settled. If lease renewal fails and the worker-side operation refuses to settle within the bounded cancellation drain, the router returns the renewal failure but keeps the durable lease for natural expiry so finalization/removal cannot race an unproven worker cleanup. A successful operation is not reported if its lease release cannot be committed; deterministic job identity allows the caller to retry safely.
|
|
42
|
+
|
|
43
|
+
## Current limitation and next integration step
|
|
44
|
+
|
|
45
|
+
Production composition prefers the routed coordinator when `/etc/agintiflow-integration/execution-worker-bindings.json` exists. It falls back to the fixed loopback client only when that file is absent. An unsafe, malformed, changed, or internally conflicting manifest fails startup; it never causes a silent downgrade. Activation still requires an initial directory assignment and real restart/cutover evidence.
|
|
46
|
+
|
|
47
|
+
The manifest is secret-free but security-sensitive and must be a canonical, non-symlinked, single-link `0600` file in a trusted, non-writable directory. Each binding contains only:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"schemaVersion": "aginti-integration-execution-worker-binding-v1",
|
|
52
|
+
"bindingId": "binding_local_workstation_01",
|
|
53
|
+
"transport": "local-loopback-http-v1",
|
|
54
|
+
"host": "127.0.0.1",
|
|
55
|
+
"port": 18130,
|
|
56
|
+
"credentialName": "execution-worker-token"
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The local binding is locked to `127.0.0.1:18130` and the existing fixed credential. LazyEdge bindings are also loopback-only from AgInTi's perspective and use reserved relay ports `18131` through `18194`, with distinct `execution-worker-binding-*` systemd credential names. Binding IDs, ports, and credential names must all be unique. Remote hosts, URLs, arbitrary ports, inline tokens, and caller-supplied credential names are rejected.
|
|
61
|
+
|
|
62
|
+
The service unit must install every named credential with `LoadCredential=`. The binding client reads only the credential selected by a branded manifest record, validates the read-only systemd credential mount and file identity, then attests the binding ID, transport, manifest digest, and exact loopback endpoint. A manifest change requires a controlled service restart.
|
|
63
|
+
|
|
64
|
+
The transition ledger currently refuses further transitions after its conservative capacity is reached rather than truncating audit history. Durable ledger segmentation is required before treating this coordinator as an unattended long-lived fleet registry.
|
|
65
|
+
|
|
66
|
+
## Verification
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm run smoke:integration-worker-directory
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The directory smoke covers workstation-to-Jetson migration, live-lease drain blocking, automatic acquire-time admission renewal, two-way rollback, finalization, retirement/removal, persisted restart recovery, expired-lease sweeping, stale and mismatched admission rejection, caller endpoint/credential rejection, invalid randomness, and fail-closed corruption, symlink, and hardlink cases.
|
|
73
|
+
|
|
74
|
+
`npm run smoke:integration-analysis-coordinator` additionally proves that an in-flight job stays pinned to worker A during a switch, logical lease-heartbeats cross the 60-second lease and five-minute admission boundaries, finalization waits for its lease, uncooperative cancellation never drops the pin early, the next job uses worker B, and capability/admission divergence fails closed without leaking a settled lease.
|
|
75
|
+
|
|
76
|
+
`npm run smoke:integration-execution-worker-binding-config` covers manifest canonicalization, local and LazyEdge port policy, uniqueness, fixed-path loading, branded bindings, and rejection of URL/token/endpoint injection.
|
|
77
|
+
|
|
78
|
+
## Local operator commands
|
|
79
|
+
|
|
80
|
+
Directory changes are local operator actions. The CLI exposes no transport or credential flags:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
aginti-worker-directory status
|
|
84
|
+
aginti-worker-directory enroll \
|
|
85
|
+
--node-id node_local_workstation_01 \
|
|
86
|
+
--binding-id binding_local_workstation_01 \
|
|
87
|
+
--platform workstation \
|
|
88
|
+
--roles execution
|
|
89
|
+
aginti-worker-directory switch \
|
|
90
|
+
--role execution \
|
|
91
|
+
--node-id node_local_workstation_01 \
|
|
92
|
+
--expected-generation 0
|
|
93
|
+
aginti-worker-directory rollback --role execution --expected-generation 2
|
|
94
|
+
aginti-worker-directory finalize --role execution --expected-generation 2
|
|
95
|
+
aginti-worker-directory retire --node-id node_old_workstation_01
|
|
96
|
+
aginti-worker-directory remove --node-id node_old_workstation_01
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Every switch and rollback requires the exact current generation. Enrollment, renewal, switching, and rollback load the fixed manifest and probe the selected worker through its branded systemd binding. Read-only status and event inspection do not require a credential. CLI output contains state and digests but no secrets.
|
|
@@ -38,6 +38,88 @@ AGINTI_EVIDENCE_SCOPE_JSON: {"mode":"task","request":"Create the requested PDF f
|
|
|
38
38
|
|
|
39
39
|
For `chat-response`, ordinary conversation and routing do not require file or command evidence. `host-managed-response` is for content-only subtasks such as a LaTeX body, translation, completion audit, or scheduled lesson where LabCanvas owns persistence, compilation, validation, and delivery. For `task`, evidence requirements are inferred only from the exact request, not from surrounding wrapper prose. Artifact requests still require real artifacts.
|
|
40
40
|
|
|
41
|
+
## Completion-Audit Artifact Coverage
|
|
42
|
+
|
|
43
|
+
A completion audit cannot mark an exact task item covered merely because its
|
|
44
|
+
candidate contains a substantive text answer. When that item directly asks to
|
|
45
|
+
create, attach, send, save, download, or otherwise deliver a file, the
|
|
46
|
+
candidate must contain compatible material artifact evidence.
|
|
47
|
+
|
|
48
|
+
Accepted evidence includes structured `files`, `artifacts`, `attachments`, or
|
|
49
|
+
deliverable records with a matching extension, MIME type, or media kind. A
|
|
50
|
+
nonempty `generated_pdf_content` field also satisfies a PDF requirement when
|
|
51
|
+
the host owns compilation. Plain prose such as `the PDF was sent` does not.
|
|
52
|
+
The boundary covers document, image, video, audio, archive, and common CAD
|
|
53
|
+
formats in English, Chinese, and Japanese.
|
|
54
|
+
|
|
55
|
+
Artifact intent remains scoped to the output. Reading `report.pdf`, analyzing
|
|
56
|
+
an attached video, or returning a summary of a video does not require the input
|
|
57
|
+
file to be returned. Explicit `do not attach a PDF` wording likewise remains a
|
|
58
|
+
text-only request. A completion audit also enforces explicit negative delivery
|
|
59
|
+
contracts. It cannot cover `create no files or attachments` when the structured
|
|
60
|
+
candidate contains outbound files or generated document/media content. A
|
|
61
|
+
format-specific exclusion such as `send PDF only; do not attach Markdown`
|
|
62
|
+
rejects only the forbidden format. If another exact request item in the same
|
|
63
|
+
packet positively requires an artifact, that independent requirement remains
|
|
64
|
+
valid instead of being erased by the first item's preference.
|
|
65
|
+
|
|
66
|
+
An invalid audit receives one bounded repair; repeated invalid coverage fails
|
|
67
|
+
closed with the exact task item retained as missing and classified as an
|
|
68
|
+
artifact requirement. LabCanvas still owns transport, compilation, validation,
|
|
69
|
+
and delivery.
|
|
70
|
+
|
|
71
|
+
## Source-Free Hypotheses And Experiment Plans
|
|
72
|
+
|
|
73
|
+
A scheduled inspiration may explicitly ask for an original, falsifiable idea
|
|
74
|
+
without supplying a fresh literature manifest. That is different from claiming
|
|
75
|
+
that a paper, benchmark, or completed experiment proves the idea. AgInTi accepts
|
|
76
|
+
the former only when the response locally labels it as the assistant's own
|
|
77
|
+
unverified hypothesis or prediction.
|
|
78
|
+
|
|
79
|
+
Numeric settings in a proposed experiment, such as group counts, durations, or
|
|
80
|
+
sampling intervals, are treated as protocol parameters rather than observed
|
|
81
|
+
benchmark results. The exception is narrow: wording that claims an experiment
|
|
82
|
+
already found, validated, achieved, or improved a result remains evidence
|
|
83
|
+
gated, as do named papers, journals, datasets, and external research tools.
|
|
84
|
+
This keeps quiet-period inspiration useful when a hosted provider hands off to
|
|
85
|
+
LocalLLM without allowing proposal language to launder unsupported facts.
|
|
86
|
+
|
|
87
|
+
## Bounded Media Source Quality
|
|
88
|
+
|
|
89
|
+
When a response-only packet explicitly asks AgInTi to summarize actual speech,
|
|
90
|
+
the packet's transcript is the speech authority. A title, description, author,
|
|
91
|
+
or hashtag may identify the source, but it cannot substitute for speech.
|
|
92
|
+
|
|
93
|
+
AgInTi checks a structured bounded transcript before accepting the response. A
|
|
94
|
+
long transcript with no content, implausibly little content, or extreme
|
|
95
|
+
repetition is treated as unusable. The backend receives one bounded repair turn
|
|
96
|
+
that must:
|
|
97
|
+
|
|
98
|
+
- disclose that the actual speech cannot be summarized reliably;
|
|
99
|
+
- avoid inferring speech, visuals, or events from metadata;
|
|
100
|
+
- label any metadata-based theme explicitly as title- or description-based;
|
|
101
|
+
- preserve the caller's JSON schema and truthful delivery flags.
|
|
102
|
+
|
|
103
|
+
If the repair repeats the unsupported summary, the turn stops with a
|
|
104
|
+
schema-compatible limitation instead of recording invented content as a
|
|
105
|
+
successful response. Reliable transcripts and requests that only ask to
|
|
106
|
+
identify supplied metadata are unaffected. Exact-source download,
|
|
107
|
+
transcription, attachments, and chat delivery remain host-owned routines.
|
|
108
|
+
|
|
109
|
+
## Fail-Closed Envelope Integrity
|
|
110
|
+
|
|
111
|
+
A response-only safety stop must preserve an explicit caller JSON envelope.
|
|
112
|
+
This applies when a backend repeats an unsupported source claim and when it
|
|
113
|
+
fails the JSON contract itself after the single bounded repair turn. The
|
|
114
|
+
fallback object keeps every required top-level key, value type, and declared
|
|
115
|
+
string enum; it places the private diagnostic in a non-enum field such as
|
|
116
|
+
`message`, `summary`, or `reason`. It never replaces a route enum with prose.
|
|
117
|
+
|
|
118
|
+
The session remains stopped and does not record `session.finished`. Envelope
|
|
119
|
+
preservation only lets the host parse and recover the task reliably; it does
|
|
120
|
+
not convert the failed turn into success, select a provider, or change any
|
|
121
|
+
LabCanvas queue, schedule, transport, or routing policy.
|
|
122
|
+
|
|
41
123
|
## Provider Tool-Batch Recovery
|
|
42
124
|
|
|
43
125
|
AgInTi requests one effectful tool call at a time, but some OpenAI-compatible
|
|
@@ -52,6 +134,63 @@ oversized batch still stop with `tool_contract_violation`. A host may resume
|
|
|
52
134
|
the same saved session with another provider after that categorized stop; it
|
|
53
135
|
must not replay the original request or duplicate earlier side effects.
|
|
54
136
|
|
|
137
|
+
## Secret-Bearing Derived Content
|
|
138
|
+
|
|
139
|
+
Private source packets can legitimately contain signed media URLs, cookies, or
|
|
140
|
+
token-shaped fields that must not be copied into a reader-facing artifact.
|
|
141
|
+
Workspace secret guards remain authoritative: the proposed write is blocked
|
|
142
|
+
before any file mutation.
|
|
143
|
+
|
|
144
|
+
That block is a content-correction event, not a request for stronger runtime
|
|
145
|
+
permission. AgInTi receives one bounded automatic recovery instruction to
|
|
146
|
+
remove the private field or replace its value with `[REDACTED]`, retain the
|
|
147
|
+
useful non-sensitive content, and write the originally requested derived
|
|
148
|
+
artifact. It must not edit the authoritative source record merely to sanitize a
|
|
149
|
+
summary, repeat the blocked value, or expose the value in its final response.
|
|
150
|
+
Other protected-path, outside-workspace, destructive, and
|
|
151
|
+
permission-sensitive blocks keep their existing approval behavior.
|
|
152
|
+
|
|
153
|
+
## Local Artifact Preview Recovery
|
|
154
|
+
|
|
155
|
+
`open_url` is only for remote `http` and `https` sources. A fallback model may
|
|
156
|
+
still copy a local artifact path into a `file://` URL when it intends to inspect
|
|
157
|
+
a generated PDF, HTML page, image, or text file. The remote-browser guard keeps
|
|
158
|
+
that URL blocked, but the mistake is a tool-selection error rather than a
|
|
159
|
+
request for stronger permission.
|
|
160
|
+
|
|
161
|
+
AgInTi receives one bounded automatic correction. It converts the local URL to
|
|
162
|
+
an authorized workspace-relative path and chooses `open_workspace_file` for a
|
|
163
|
+
single rendered file, `preview_workspace` for a static site or directory, or
|
|
164
|
+
`read_file` when textual inspection is enough. It must not retry `open_url`,
|
|
165
|
+
start a temporary localhost server, or ask the user to elevate the sandbox.
|
|
166
|
+
The replacement workspace tool still enforces its normal path and read-root
|
|
167
|
+
rules, so an outside-workspace or protected target remains blocked.
|
|
168
|
+
|
|
169
|
+
## Current-Turn Response Fidelity
|
|
170
|
+
|
|
171
|
+
Response-only prompts contain host control metadata and may contain prior
|
|
172
|
+
bot-authored messages for conversational continuity. Neither is an answer to
|
|
173
|
+
the current inbound message. AgInTiFlow therefore rejects two exact replay
|
|
174
|
+
forms before accepting completion:
|
|
175
|
+
|
|
176
|
+
- a returned JSON object that is identical to an
|
|
177
|
+
`AGINTI_EVIDENCE_SCOPE_JSON` control envelope;
|
|
178
|
+
- a substantive primary response field that exactly repeats a prior
|
|
179
|
+
`is_self: true` chat message while the current message asks something else.
|
|
180
|
+
|
|
181
|
+
The backend receives one bounded repair turn that identifies the current
|
|
182
|
+
message as authoritative and preserves any explicit JSON key and type
|
|
183
|
+
contract. A current message that explicitly requests a verbatim repetition is
|
|
184
|
+
accepted. Merely quoting an earlier bot message to ask about it is not a repeat
|
|
185
|
+
request.
|
|
186
|
+
|
|
187
|
+
The replay check runs again after transcript-quality and source-free-evidence
|
|
188
|
+
repairs. This prevents one validator from replacing a rejected answer with
|
|
189
|
+
stale context that a later stage would otherwise accept. A repeated replay
|
|
190
|
+
fails closed in the caller's parseable envelope, leaves the session stopped,
|
|
191
|
+
and never records `session.finished`. This does not alter LabCanvas routing,
|
|
192
|
+
provider priority, queues, schedules, transports, or chat coalescing.
|
|
193
|
+
|
|
55
194
|
## Local Context Recovery
|
|
56
195
|
|
|
57
196
|
LocalLLM planning compacts oversized goals to a bounded head-and-tail representation. Runtime compaction retains the first request and latest interruptions. A `LocalContextBudgetError` triggers one compact-and-retry cycle at the same step and records private recovery events. It does not authorize replaying task side effects.
|