@lazyingart/agintiflow 0.20.291 → 0.20.296
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/docs/supervision-campaign-ledger.md +118 -0
- package/package.json +1 -1
- package/scripts/smoke-coding-tools.js +39 -0
- package/scripts/smoke-dynamic-step-budget.js +86 -1
- package/scripts/smoke-integration-storage-authority.js +1 -0
- package/scripts/smoke-model-roles.js +43 -1
- package/scripts/smoke-progressive-tool-selection.js +204 -0
- package/scripts/smoke-scs-evidence-visibility.js +12 -0
- package/scripts/smoke-skills.js +6 -0
- package/scripts/smoke-truthful-completion.js +169 -0
- package/scripts/smoke-workspace-secret-false-positive.js +20 -0
- package/skills/security-review/SKILL.md +2 -0
- package/src/agent-runner.js +496 -15
- package/src/integration-storage-authority.js +17 -0
- package/src/permission-advice.js +8 -1
- package/src/progressive-tool-selection.js +27 -10
- package/src/redaction.js +1 -1
- package/src/scs-evidence.js +201 -7
- package/src/workspace-tools.js +1 -1
|
@@ -431,3 +431,121 @@ punctuation search literal, added regression coverage, passed four unit tests,
|
|
|
431
431
|
and committed a clean tree at `b956470`. The independent migration-safety
|
|
432
432
|
contract also passed. The complete npm suite passes, and the runtime fix is
|
|
433
433
|
released in AgInTiFlow `0.20.291`.
|
|
434
|
+
|
|
435
|
+
### Explicit test evidence and security-review completion
|
|
436
|
+
|
|
437
|
+
`security-labshare-050` asked a fresh DeepSeek Pro session to harden a small
|
|
438
|
+
standard-library laboratory service from an imperfect, outcome-level prompt.
|
|
439
|
+
The agent removed a default credential, bounded artifact and dataset paths,
|
|
440
|
+
kept export execution shell-free, redacted audit credentials, protected the
|
|
441
|
+
public status response, added focused tests, and committed a clean repair. The
|
|
442
|
+
hidden acceptance contract then exposed a remaining audit-log injection path:
|
|
443
|
+
newlines in an actor or artifact field could forge additional physical records.
|
|
444
|
+
|
|
445
|
+
During the retained-session repair, AgInTi fixed the production source but
|
|
446
|
+
committed it before adding the explicitly requested regression test. This was a
|
|
447
|
+
runtime contract defect, not a model-quality failure. The mutation parser did
|
|
448
|
+
not recognize contextual requests such as "add a regression test," and the SCS
|
|
449
|
+
evidence contract did not convert "run the tests" into a fresh test obligation.
|
|
450
|
+
Consequently, source and Git evidence could satisfy the phase too early.
|
|
451
|
+
|
|
452
|
+
AgInTiFlow now recognizes explicit English and Chinese test-file mutations,
|
|
453
|
+
requires fresh test evidence for explicit run/rerun-test requests, and keeps
|
|
454
|
+
task-owned commit completion closed until that evidence exists. The regression
|
|
455
|
+
suite reproduces the stale persisted contract and proves no commit is offered
|
|
456
|
+
after source mutation but before a fresh passing test. It avoids broad keyword
|
|
457
|
+
matching, so phrases such as "create a canvas preview for this smoke test" do
|
|
458
|
+
not invent a test-file mutation.
|
|
459
|
+
|
|
460
|
+
The same release accepts genuinely observational Git evidence when no
|
|
461
|
+
consequential Git action is required, permits only the exact bounded Python
|
|
462
|
+
cache-cleanup forms used by project hygiene, and treats masked values such as
|
|
463
|
+
`token=***` as safe status evidence without weakening detection of real
|
|
464
|
+
credential assignments. The security-review skill now covers control-character
|
|
465
|
+
log injection and requires a standalone note to distinguish deployment
|
|
466
|
+
boundary, threat model, controls, residual risks, non-goals, and verification.
|
|
467
|
+
|
|
468
|
+
The retained session completed `SECURITY.md` after a valid secret-content
|
|
469
|
+
block, passed the exact hidden contract and all 13 visible tests, and left a
|
|
470
|
+
clean target repository at `ef3c099`. Focused regressions and the complete npm
|
|
471
|
+
suite pass. These runtime and skill fixes are released in AgInTiFlow
|
|
472
|
+
`0.20.292`.
|
|
473
|
+
|
|
474
|
+
### Retained storage live authority poisoning
|
|
475
|
+
|
|
476
|
+
`retained-storage-live-poison-047` exercised the native integration storage
|
|
477
|
+
authority after independent audit addenda, using a dictated but realistic
|
|
478
|
+
operator prompt. The defect was an AgInTiFlow core integration-storage
|
|
479
|
+
authority gap: live retained handles and named bindings were mostly checked,
|
|
480
|
+
but the public limitation record omitted the generic `resolveBeneath:false`
|
|
481
|
+
fact, and protected file success paths could return after cleanup awaits
|
|
482
|
+
without one final permanent-poison check.
|
|
483
|
+
|
|
484
|
+
The storage authority now reports `procfsRequired:true`,
|
|
485
|
+
`resolveBeneath:false`, and `noXdev:false` truthfully across retained
|
|
486
|
+
directory/file/lock limitation records. Live retained owner, mode, fstat, and
|
|
487
|
+
named-binding divergence remains `INTEGRATION_STORAGE_POISONED`, not an
|
|
488
|
+
availability failure, and protected read/write/sync paths check permanent
|
|
489
|
+
poison again immediately before returning success.
|
|
490
|
+
|
|
491
|
+
External verification used the module-mocked retained-storage smoke for live
|
|
492
|
+
fstat, owner, mode, named-binding, concurrent poison, close/admission, cleanup,
|
|
493
|
+
and residual-FD adversaries. Retained durable-common, retained file-lock,
|
|
494
|
+
production-mount, integration authorities, runtime authority,
|
|
495
|
+
session-persistence, syntax, and whitespace checks passed on the working tree.
|
|
496
|
+
|
|
497
|
+
### Authoritative read-only routine first
|
|
498
|
+
|
|
499
|
+
`authoritative-readonly-routine-001` reproduced the LabCanvas status failure
|
|
500
|
+
from sessions `web-agent-labcanvas-cb6fe7cd-3464-42f7-99e4-b24c376e0115` and
|
|
501
|
+
`web-agent-labcanvas-6d0b04bc-79e8-4fd8-87b4-4d8e651dcd72`. The first session
|
|
502
|
+
had no selected routine and spent 25 model requests and 23 tools on broad
|
|
503
|
+
workspace/private-store exploration. The second session received a disclosed
|
|
504
|
+
canonical read-only routine, but AgInTiFlow still kept
|
|
505
|
+
`requiredProjectCommands` empty, then continued through 32 model requests and
|
|
506
|
+
32 tools after the status snapshot.
|
|
507
|
+
|
|
508
|
+
The defect was an AgInTiFlow core execution-contract/SCS handoff gap, with a
|
|
509
|
+
separate upstream routine-disclosure improvement already handled by
|
|
510
|
+
AgenticApp. AgInTiFlow now promotes an authoritative read-only routine's first
|
|
511
|
+
safe command into the exact required command batch, records an observed
|
|
512
|
+
nonzero status snapshot as read-only evidence instead of a failed verifier,
|
|
513
|
+
retains forbidden raw/private evidence scopes, and closes the following turn to
|
|
514
|
+
`finish` once normal SCS evidence is sufficient.
|
|
515
|
+
|
|
516
|
+
The focused regression uses a weak status prompt and a project-neutral
|
|
517
|
+
`sample-status` routine. It proves the first model turn sees only the exact
|
|
518
|
+
status command plus `finish`, a JSON status command exiting 1 still satisfies
|
|
519
|
+
the read-only evidence contract, the second turn is finish-only, and no
|
|
520
|
+
private/raw exploratory command is dispatched. `npm run smoke:progressive-tools`,
|
|
521
|
+
`npm run smoke:scs-evidence`, `npm run smoke:truthful-completion`, and
|
|
522
|
+
`npm run check` pass on the working tree.
|
|
523
|
+
|
|
524
|
+
Fresh compact retest `web-agent-labcanvas-2ebe5aef-83c8-4134-abcd-bec630a4ecb9`
|
|
525
|
+
reduced the flow to 6 model requests and 5 tool starts and used the canonical
|
|
526
|
+
compact status command, but both valid `finish` calls were rejected because the
|
|
527
|
+
human-facing status said `Still retrying: echomind_daily_pdf` and named a
|
|
528
|
+
`next attempt`. That was still an AgInTiFlow core completion-semantic defect:
|
|
529
|
+
the validator treated an observed external retry state as the agent promising
|
|
530
|
+
unfinished work. The completion predicate now rejects pending work only when it
|
|
531
|
+
is tied to the current task/report/validation/change or an agent-promised
|
|
532
|
+
future action. Read-only status answers may truthfully report external
|
|
533
|
+
pending/retrying/next-attempt state after sufficient evidence exists.
|
|
534
|
+
|
|
535
|
+
Installed retest `web-agent-labcanvas-5e6eb6b6-fb8d-419d-a549-aa4098252064`
|
|
536
|
+
then proved the next core gap. AgInTiFlow ran the same canonical compact
|
|
537
|
+
health command exactly once and avoided raw/private exploration, but the
|
|
538
|
+
finish-only verified-completion turns kept returning empty content and no tool
|
|
539
|
+
calls with `finishReason=length` at the inherited 768-token output cap. After
|
|
540
|
+
the bounded empty-response repair was exhausted, the runtime emitted the
|
|
541
|
+
generic verified fallback `Evidence: command` instead of summarizing the
|
|
542
|
+
authoritative JSON status snapshot.
|
|
543
|
+
|
|
544
|
+
That defect is still AgInTiFlow core completion-loop behavior, not AgenticApp
|
|
545
|
+
or a WeChat-specific routine. Verified-completion turns now have their own
|
|
546
|
+
final-answer output floor and explicit finish-only instruction. If a provider
|
|
547
|
+
still returns empty final responses after verified evidence exists, the
|
|
548
|
+
fallback derives a concise public answer from the successful bounded command
|
|
549
|
+
output. JSON status snapshots summarize visible delivered/retrying schedule
|
|
550
|
+
states, ingress reachability, queue counts, and top-level health while keeping
|
|
551
|
+
the original SCS checks for genuinely unfinished agent work.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.296",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -1680,6 +1680,45 @@ try {
|
|
|
1680
1680
|
mixedValidationCleanupAdvice.autoRecover === true,
|
|
1681
1681
|
"unrequested cleanup should be skipped without pausing substantive work"
|
|
1682
1682
|
);
|
|
1683
|
+
const pythonCacheCleanupArgs = {
|
|
1684
|
+
command:
|
|
1685
|
+
"find . -type d -name __pycache__ -prune -exec rm -rf {} + ; find . -type f -name '*.pyc' -delete; git status --short",
|
|
1686
|
+
};
|
|
1687
|
+
assert(
|
|
1688
|
+
isUnrequestedCleanupCommand(
|
|
1689
|
+
"run_command",
|
|
1690
|
+
pythonCacheCleanupArgs,
|
|
1691
|
+
{ goal: "Repair the service, run its tests, and commit the intentional work." },
|
|
1692
|
+
{}
|
|
1693
|
+
),
|
|
1694
|
+
"post-acceptance Python cache deletion was not recognized as optional housekeeping"
|
|
1695
|
+
);
|
|
1696
|
+
const pythonCacheCleanupAdvice = buildPermissionAdvice({
|
|
1697
|
+
toolName: "run_command",
|
|
1698
|
+
args: pythonCacheCleanupArgs,
|
|
1699
|
+
guard: {
|
|
1700
|
+
category: "destructive",
|
|
1701
|
+
reason: "Destructive shell commands require Allow destructive actions.",
|
|
1702
|
+
},
|
|
1703
|
+
config: {
|
|
1704
|
+
...dockerWorkspacePolicy,
|
|
1705
|
+
goal: "Repair the service, run its tests, and commit the intentional work.",
|
|
1706
|
+
},
|
|
1707
|
+
state: { sessionId: "coding-python-cache-cleanup-smoke" },
|
|
1708
|
+
});
|
|
1709
|
+
assert(
|
|
1710
|
+
pythonCacheCleanupAdvice.autoRecover === true,
|
|
1711
|
+
"ignored Python cache cleanup should be skipped without pausing a completed task"
|
|
1712
|
+
);
|
|
1713
|
+
assert(
|
|
1714
|
+
!isUnrequestedCleanupCommand(
|
|
1715
|
+
"run_command",
|
|
1716
|
+
{ command: "find . -type f -delete" },
|
|
1717
|
+
{ goal: "Repair the service and commit the result." },
|
|
1718
|
+
{}
|
|
1719
|
+
),
|
|
1720
|
+
"broad find deletion was incorrectly classified as optional Python cache cleanup"
|
|
1721
|
+
);
|
|
1683
1722
|
assert(
|
|
1684
1723
|
!isUnrequestedCleanupCommand(
|
|
1685
1724
|
"run_command",
|
|
@@ -9251,7 +9251,7 @@ try {
|
|
|
9251
9251
|
const verifiedCompletionText = JSON.stringify(verifiedCompletionRequest?.messages || []);
|
|
9252
9252
|
assert(
|
|
9253
9253
|
verifiedCompletionRequest?.mode === "verified-completion" &&
|
|
9254
|
-
verifiedCompletionRequest?.maxOutputTokens ===
|
|
9254
|
+
verifiedCompletionRequest?.maxOutputTokens === 2048,
|
|
9255
9255
|
"fresh passing evidence did not narrow the final response turn"
|
|
9256
9256
|
);
|
|
9257
9257
|
assert(
|
|
@@ -13546,6 +13546,91 @@ Do not prefix, suffix, wrap, redirect, pipe, or combine that validator command.`
|
|
|
13546
13546
|
"a partial multi-artifact task entered task-owned Git completion before its deliverables existed"
|
|
13547
13547
|
);
|
|
13548
13548
|
|
|
13549
|
+
const securityWorkspace = path.join(tempRoot, "security-test-before-commit");
|
|
13550
|
+
await fs.mkdir(securityWorkspace, { recursive: true });
|
|
13551
|
+
await fs.writeFile(path.join(securityWorkspace, "labshare.py"), "def record_access():\n pass\n");
|
|
13552
|
+
const securityGoal = [
|
|
13553
|
+
"Independent acceptance failed: audit fields permit newline log injection.",
|
|
13554
|
+
"Add a focused regression test, fix the root cause, run the full visible tests,",
|
|
13555
|
+
"review the diff, and commit only this corrective work.",
|
|
13556
|
+
].join(" ");
|
|
13557
|
+
const securityState = {
|
|
13558
|
+
goal: securityGoal,
|
|
13559
|
+
commandCwd: securityWorkspace,
|
|
13560
|
+
plan: "Patch the source and regression test, run the full suite, then commit the verified work.",
|
|
13561
|
+
messages: [
|
|
13562
|
+
{
|
|
13563
|
+
role: "tool",
|
|
13564
|
+
content: JSON.stringify({
|
|
13565
|
+
ok: true,
|
|
13566
|
+
toolName: "apply_patch",
|
|
13567
|
+
path: "labshare.py",
|
|
13568
|
+
goalRevision: 1,
|
|
13569
|
+
projectMutationRevision: 1,
|
|
13570
|
+
}),
|
|
13571
|
+
},
|
|
13572
|
+
{
|
|
13573
|
+
role: "tool",
|
|
13574
|
+
content: JSON.stringify({
|
|
13575
|
+
ok: true,
|
|
13576
|
+
toolName: "run_command",
|
|
13577
|
+
exitCode: 0,
|
|
13578
|
+
args: { command: "git status --short" },
|
|
13579
|
+
stdout: " M labshare.py\n",
|
|
13580
|
+
goalRevision: 1,
|
|
13581
|
+
projectMutationRevision: 1,
|
|
13582
|
+
}),
|
|
13583
|
+
},
|
|
13584
|
+
],
|
|
13585
|
+
meta: {
|
|
13586
|
+
taskProfile: "security",
|
|
13587
|
+
goalContract: {
|
|
13588
|
+
revision: 1,
|
|
13589
|
+
currentRequest: securityGoal,
|
|
13590
|
+
taskGoal: securityGoal,
|
|
13591
|
+
activeGoal: securityGoal,
|
|
13592
|
+
activeGoalRevision: 1,
|
|
13593
|
+
history: [{ revision: 1, refreshExecutionContract: true }],
|
|
13594
|
+
lifecycle: [{ at: new Date(Date.now() - 2000).toISOString() }],
|
|
13595
|
+
},
|
|
13596
|
+
// Reproduce the pre-fix persisted contract that recognized a mutation
|
|
13597
|
+
// but failed to classify the requested regression test as file work.
|
|
13598
|
+
activeExecutionContract: {
|
|
13599
|
+
revision: 1,
|
|
13600
|
+
startedMutationRevision: 0,
|
|
13601
|
+
materialMutationRevision: 1,
|
|
13602
|
+
requiresWorkspaceMutation: true,
|
|
13603
|
+
requiresFileMutation: false,
|
|
13604
|
+
},
|
|
13605
|
+
projectVerification: {
|
|
13606
|
+
mutationRevision: 1,
|
|
13607
|
+
mutationHistory: [
|
|
13608
|
+
{
|
|
13609
|
+
revision: 1,
|
|
13610
|
+
at: new Date().toISOString(),
|
|
13611
|
+
toolName: "apply_patch",
|
|
13612
|
+
paths: ["labshare.py"],
|
|
13613
|
+
goalRevision: 1,
|
|
13614
|
+
},
|
|
13615
|
+
],
|
|
13616
|
+
},
|
|
13617
|
+
},
|
|
13618
|
+
};
|
|
13619
|
+
const securityCompletionContract = completionTaskContract(
|
|
13620
|
+
{ goal: securityGoal, taskProfile: "security", commandCwd: securityWorkspace },
|
|
13621
|
+
securityState
|
|
13622
|
+
);
|
|
13623
|
+
const preTestSecurityRuntime = nextStepRuntimeConfig(
|
|
13624
|
+
{ goal: securityGoal, taskProfile: "security", commandCwd: securityWorkspace },
|
|
13625
|
+
securityState
|
|
13626
|
+
);
|
|
13627
|
+
assert(
|
|
13628
|
+
securityCompletionContract.requiresFileMutation === true &&
|
|
13629
|
+
securityCompletionContract.requiredEvidence.some((item) => item.category === "test") &&
|
|
13630
|
+
preTestSecurityRuntime.taskOwnedCommitPending !== true,
|
|
13631
|
+
"a regression-test repair entered task-owned Git completion before fresh test evidence"
|
|
13632
|
+
);
|
|
13633
|
+
|
|
13549
13634
|
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
13550
13635
|
console.log("smoke-dynamic-step-budget ok");
|
|
13551
13636
|
} catch (error) {
|
|
@@ -538,6 +538,7 @@ async function main() {
|
|
|
538
538
|
assert.equal(authority.attestation.limitations.preEnablePrimitive, true);
|
|
539
539
|
assert.equal(authority.attestation.limitations.procfsRequired, true);
|
|
540
540
|
assert.equal(authority.attestation.limitations.openat2ResolveBeneath, false);
|
|
541
|
+
assert.equal(authority.attestation.limitations.resolveBeneath, false);
|
|
541
542
|
assert.equal(authority.attestation.limitations.noXdev, false);
|
|
542
543
|
|
|
543
544
|
const identity = await authority.identity();
|
|
@@ -930,6 +930,22 @@ const refreshedSecurityContract = deriveScsTaskContract({
|
|
|
930
930
|
goal: "Fix the security issue and run the regression tests.",
|
|
931
931
|
taskProfile: "security",
|
|
932
932
|
});
|
|
933
|
+
const correctiveSecurityContract = deriveScsTaskContract({
|
|
934
|
+
goal: [
|
|
935
|
+
"Independent acceptance failed: audit fields permit newline log injection.",
|
|
936
|
+
"Reopen the exact repair, add a focused regression test, fix the root cause,",
|
|
937
|
+
"run the full visible tests, review the diff, and commit only this corrective work.",
|
|
938
|
+
].join(" "),
|
|
939
|
+
taskProfile: "security",
|
|
940
|
+
});
|
|
941
|
+
assert(
|
|
942
|
+
correctiveSecurityContract.requiresWorkspaceMutation === true &&
|
|
943
|
+
correctiveSecurityContract.requiresFileMutation === true &&
|
|
944
|
+
correctiveSecurityContract.requiredEvidence.some((item) => item.category === "file") &&
|
|
945
|
+
correctiveSecurityContract.requiredEvidence.some((item) => item.category === "test") &&
|
|
946
|
+
correctiveSecurityContract.requiredGitActions.includes("commit"),
|
|
947
|
+
"a requested regression-test repair could enter Git completion without fresh file and test evidence"
|
|
948
|
+
);
|
|
933
949
|
const staleContractFinish = await reviewScsFinish(
|
|
934
950
|
{ mock: true },
|
|
935
951
|
{ provider: "mock", model: "mock-agent", taskProfile: "security" },
|
|
@@ -964,6 +980,11 @@ const staleContractFinish = await reviewScsFinish(
|
|
|
964
980
|
exitCode: 0,
|
|
965
981
|
args: { command: "python -m unittest discover -s tests -v" },
|
|
966
982
|
stdout: "Ran 10 tests\nOK",
|
|
983
|
+
projectTest: {
|
|
984
|
+
passed: true,
|
|
985
|
+
command: "python -m unittest discover -s tests -v",
|
|
986
|
+
mutationRevision: 0,
|
|
987
|
+
},
|
|
967
988
|
}),
|
|
968
989
|
},
|
|
969
990
|
],
|
|
@@ -1056,6 +1077,20 @@ assert(
|
|
|
1056
1077
|
evaluateScsEvidence(commitContract, gitStatusLedger).missingGitActions.includes("commit"),
|
|
1057
1078
|
"the completion deficit did not identify the missing commit"
|
|
1058
1079
|
);
|
|
1080
|
+
const readOnlyGitContract = {
|
|
1081
|
+
requiresExternalEvidence: true,
|
|
1082
|
+
requiredEvidence: [
|
|
1083
|
+
{ id: "command", category: "command", description: "read-only verification" },
|
|
1084
|
+
{ id: "git", category: "git", description: "existing commit verification" },
|
|
1085
|
+
],
|
|
1086
|
+
requiredToolCalls: [],
|
|
1087
|
+
requiredGitActions: [],
|
|
1088
|
+
requiredProjectCommands: [],
|
|
1089
|
+
};
|
|
1090
|
+
assert(
|
|
1091
|
+
evaluateScsEvidence(readOnlyGitContract, gitStatusLedger).ok,
|
|
1092
|
+
"read-only Git evidence was rejected when the contract required no consequential Git action"
|
|
1093
|
+
);
|
|
1059
1094
|
const committedEvaluation = evaluateScsEvidence(commitContract, gitCommitLedger);
|
|
1060
1095
|
assert(
|
|
1061
1096
|
committedEvaluation.missingGitActions.length === 0 &&
|
|
@@ -1231,7 +1266,14 @@ const checkedCodeEval = evaluateScsEvidence(
|
|
|
1231
1266
|
messages: [
|
|
1232
1267
|
{
|
|
1233
1268
|
role: "tool",
|
|
1234
|
-
content: JSON.stringify({
|
|
1269
|
+
content: JSON.stringify({
|
|
1270
|
+
toolName: "run_command",
|
|
1271
|
+
ok: true,
|
|
1272
|
+
exitCode: 0,
|
|
1273
|
+
args: { command: "npm test" },
|
|
1274
|
+
stdout: "ok",
|
|
1275
|
+
projectTest: { passed: true, command: "npm test", mutationRevision: 0 },
|
|
1276
|
+
}),
|
|
1235
1277
|
},
|
|
1236
1278
|
],
|
|
1237
1279
|
},
|
|
@@ -5501,6 +5501,55 @@ assertStrict.equal(
|
|
|
5501
5501
|
undefined,
|
|
5502
5502
|
"an explicit task artifact root still activated generic repository grounding"
|
|
5503
5503
|
);
|
|
5504
|
+
const scopedPathWriteDescriptor = {
|
|
5505
|
+
type: "function",
|
|
5506
|
+
function: {
|
|
5507
|
+
name: "write_file",
|
|
5508
|
+
description: "Write one workspace file.",
|
|
5509
|
+
parameters: {
|
|
5510
|
+
type: "object",
|
|
5511
|
+
properties: {
|
|
5512
|
+
path: { type: "string" },
|
|
5513
|
+
content: { type: "string" },
|
|
5514
|
+
},
|
|
5515
|
+
required: ["path", "content"],
|
|
5516
|
+
additionalProperties: false,
|
|
5517
|
+
},
|
|
5518
|
+
},
|
|
5519
|
+
};
|
|
5520
|
+
const scopedPathSurface = [
|
|
5521
|
+
...allTools.filter((item) => item.function.name !== "write_file"),
|
|
5522
|
+
scopedPathWriteDescriptor,
|
|
5523
|
+
];
|
|
5524
|
+
const scopedPathTools = selectProgressiveTools(scopedPathSurface, {
|
|
5525
|
+
config: {
|
|
5526
|
+
provider: "deepseek",
|
|
5527
|
+
progressiveTools: true,
|
|
5528
|
+
scopedArtifactTask: true,
|
|
5529
|
+
scopedArtifactRoot: scopedRuntime.scopedArtifactRoot,
|
|
5530
|
+
commandCwd: "/workspace",
|
|
5531
|
+
},
|
|
5532
|
+
goal: scopedRuntimePrompt,
|
|
5533
|
+
profile: "auto",
|
|
5534
|
+
messages: [{ role: "user", content: scopedRuntimePrompt }],
|
|
5535
|
+
});
|
|
5536
|
+
const scopedWritePathPattern = scopedPathTools.find(
|
|
5537
|
+
(item) => item.function.name === "write_file"
|
|
5538
|
+
)?.function?.parameters?.properties?.path?.pattern;
|
|
5539
|
+
assert(scopedWritePathPattern, "scoped artifact write tool omitted its path contract");
|
|
5540
|
+
const scopedWritePathRegex = new RegExp(scopedWritePathPattern);
|
|
5541
|
+
assert(
|
|
5542
|
+
scopedWritePathRegex.test("output/wechat_worker/task/report.md"),
|
|
5543
|
+
"scoped artifact write contract rejected its workspace-relative path"
|
|
5544
|
+
);
|
|
5545
|
+
assert(
|
|
5546
|
+
scopedWritePathRegex.test("/workspace/output/wechat_worker/task/report.md"),
|
|
5547
|
+
"scoped artifact write contract rejected the host-supplied exact absolute path"
|
|
5548
|
+
);
|
|
5549
|
+
assert(
|
|
5550
|
+
!scopedWritePathRegex.test("/workspace/output/wechat_worker/other/report.md"),
|
|
5551
|
+
"scoped artifact write contract accepted a sibling task path"
|
|
5552
|
+
);
|
|
5504
5553
|
|
|
5505
5554
|
const longScopedTaskRoot = path.join(repoRoot, "output/wechat_worker/long-scoped-task");
|
|
5506
5555
|
const longScopedScopeLine = `AGINTI_EVIDENCE_SCOPE_JSON: ${JSON.stringify({
|
|
@@ -7288,6 +7337,160 @@ async function runToolContractCase({
|
|
|
7288
7337
|
}
|
|
7289
7338
|
}
|
|
7290
7339
|
|
|
7340
|
+
function weakAuthoritativeRoutineGoal(command) {
|
|
7341
|
+
return [
|
|
7342
|
+
"AGINTI_EVIDENCE_SCOPE_JSON: {\"mode\":\"task\",\"request\":\"tell me phone and schedule status, read-only, dont send or change anything\"}",
|
|
7343
|
+
"Matched established routines",
|
|
7344
|
+
`- \`sample-status\` ready=true; commands=[${JSON.stringify(command)}, "node scripts/secondary-status.js --json"]; outputs=["status"]; guidance=For a read-only phone, message-intake, queue, or schedule question, run the canonical \`${command}\` command first. Treat the current status snapshot as authoritative. Do not inspect raw private stores, and do not send or mutate anything.`,
|
|
7345
|
+
"Operating contract",
|
|
7346
|
+
"- Forbidden evidence: raw/private ledgers are out of scope for this read-only answer.",
|
|
7347
|
+
].join("\n");
|
|
7348
|
+
}
|
|
7349
|
+
|
|
7350
|
+
const authoritativeRoutineCommand = "node scripts/status.js status --json";
|
|
7351
|
+
const authoritativeRoutineGoal = weakAuthoritativeRoutineGoal(
|
|
7352
|
+
authoritativeRoutineCommand
|
|
7353
|
+
);
|
|
7354
|
+
const authoritativeRoutineContract = completionTaskContract(
|
|
7355
|
+
{
|
|
7356
|
+
goal: authoritativeRoutineGoal,
|
|
7357
|
+
taskProfile: "auto",
|
|
7358
|
+
},
|
|
7359
|
+
{
|
|
7360
|
+
goal: authoritativeRoutineGoal,
|
|
7361
|
+
meta: {
|
|
7362
|
+
taskProfile: "auto",
|
|
7363
|
+
goalContract: {
|
|
7364
|
+
revision: 1,
|
|
7365
|
+
currentRequest: authoritativeRoutineGoal,
|
|
7366
|
+
},
|
|
7367
|
+
},
|
|
7368
|
+
}
|
|
7369
|
+
);
|
|
7370
|
+
assertStrict.deepEqual(
|
|
7371
|
+
authoritativeRoutineContract.requiredProjectCommands,
|
|
7372
|
+
[authoritativeRoutineCommand],
|
|
7373
|
+
"authoritative read-only routine was not promoted to an exact required command"
|
|
7374
|
+
);
|
|
7375
|
+
assertStrict.deepEqual(
|
|
7376
|
+
authoritativeRoutineContract.authoritativeRoutine?.forbiddenEvidenceScopes,
|
|
7377
|
+
["private", "raw"],
|
|
7378
|
+
"authoritative read-only routine did not retain forbidden evidence scopes"
|
|
7379
|
+
);
|
|
7380
|
+
|
|
7381
|
+
const hostedRequiredRoutineTools = selectProgressiveTools(allTools, {
|
|
7382
|
+
config: {
|
|
7383
|
+
provider: "deepseek",
|
|
7384
|
+
toolSurfacePolicy: "full",
|
|
7385
|
+
requiredProjectCommandPending: true,
|
|
7386
|
+
requiredProjectCommand: authoritativeRoutineCommand,
|
|
7387
|
+
},
|
|
7388
|
+
goal: authoritativeRoutineGoal,
|
|
7389
|
+
profile: "auto",
|
|
7390
|
+
});
|
|
7391
|
+
sameNames(
|
|
7392
|
+
hostedRequiredRoutineTools,
|
|
7393
|
+
["run_command", "finish"],
|
|
7394
|
+
"hosted/full policy did not constrain an authoritative routine first command"
|
|
7395
|
+
);
|
|
7396
|
+
assertStrict.deepEqual(
|
|
7397
|
+
enumFor(hostedRequiredRoutineTools, "run_command", "command"),
|
|
7398
|
+
[authoritativeRoutineCommand],
|
|
7399
|
+
"authoritative routine command was not exposed as an exact enum"
|
|
7400
|
+
);
|
|
7401
|
+
|
|
7402
|
+
let authoritativeRoutineStep = 0;
|
|
7403
|
+
const authoritativeRoutineRun = await runToolContractCase({
|
|
7404
|
+
id: "authoritative-readonly-routine",
|
|
7405
|
+
provider: "deepseek",
|
|
7406
|
+
profile: "auto",
|
|
7407
|
+
goal: authoritativeRoutineGoal,
|
|
7408
|
+
allowShellTool: true,
|
|
7409
|
+
maxSteps: 4,
|
|
7410
|
+
toolCalls: [
|
|
7411
|
+
contractCall("unused-authoritative", "finish", { result: "unused" }),
|
|
7412
|
+
],
|
|
7413
|
+
expectSuccess: true,
|
|
7414
|
+
setupWorkspace: async (workspace) => {
|
|
7415
|
+
await fs.mkdir(path.join(workspace, "scripts"), { recursive: true });
|
|
7416
|
+
await fs.writeFile(
|
|
7417
|
+
path.join(workspace, "scripts", "status.js"),
|
|
7418
|
+
[
|
|
7419
|
+
"console.log(JSON.stringify({ phone: 'login-required', schedule: 'current', authoritative: true }));",
|
|
7420
|
+
"process.exit(1);",
|
|
7421
|
+
"",
|
|
7422
|
+
].join("\n"),
|
|
7423
|
+
"utf8"
|
|
7424
|
+
);
|
|
7425
|
+
},
|
|
7426
|
+
responseFactory: ({ payload }) => {
|
|
7427
|
+
authoritativeRoutineStep += 1;
|
|
7428
|
+
const offered = Array.isArray(payload.tools) ? names(payload.tools) : [];
|
|
7429
|
+
if (authoritativeRoutineStep === 1) {
|
|
7430
|
+
assertStrict.deepEqual(
|
|
7431
|
+
offered,
|
|
7432
|
+
["run_command", "finish"],
|
|
7433
|
+
"first authoritative routine turn exposed exploratory tools"
|
|
7434
|
+
);
|
|
7435
|
+
assertStrict.deepEqual(
|
|
7436
|
+
enumFor(payload.tools, "run_command", "command"),
|
|
7437
|
+
[authoritativeRoutineCommand],
|
|
7438
|
+
"first authoritative routine turn did not constrain the exact command"
|
|
7439
|
+
);
|
|
7440
|
+
return assistantWithToolCalls([
|
|
7441
|
+
contractCall("run-authoritative-status", "run_command", {
|
|
7442
|
+
command: authoritativeRoutineCommand,
|
|
7443
|
+
}),
|
|
7444
|
+
]);
|
|
7445
|
+
}
|
|
7446
|
+
assertStrict.deepEqual(
|
|
7447
|
+
offered,
|
|
7448
|
+
["finish"],
|
|
7449
|
+
"authoritative routine observation did not shrink the next turn to finish-only"
|
|
7450
|
+
);
|
|
7451
|
+
return assistantWithToolCalls([
|
|
7452
|
+
contractCall("finish-authoritative-status", "finish", {
|
|
7453
|
+
result: "The authoritative status snapshot says phone login is required and the schedule snapshot is current.",
|
|
7454
|
+
}),
|
|
7455
|
+
]);
|
|
7456
|
+
},
|
|
7457
|
+
});
|
|
7458
|
+
assertStrict.equal(
|
|
7459
|
+
authoritativeRoutineRun.requests.length,
|
|
7460
|
+
2,
|
|
7461
|
+
"authoritative routine flow used more than two model requests"
|
|
7462
|
+
);
|
|
7463
|
+
assertStrict.equal(
|
|
7464
|
+
authoritativeRoutineRun.events.filter(
|
|
7465
|
+
(event) =>
|
|
7466
|
+
event.type === "tool.started" &&
|
|
7467
|
+
event.data?.toolName !== "finish"
|
|
7468
|
+
).length,
|
|
7469
|
+
1,
|
|
7470
|
+
"authoritative routine flow dispatched more than the canonical command"
|
|
7471
|
+
);
|
|
7472
|
+
assert(
|
|
7473
|
+
authoritativeRoutineRun.events.some(
|
|
7474
|
+
(event) =>
|
|
7475
|
+
event.type === "tool.completed" &&
|
|
7476
|
+
event.data?.toolName === "run_command" &&
|
|
7477
|
+
event.data?.ok === false &&
|
|
7478
|
+
event.data?.authoritativeReadOnlyRoutine === true &&
|
|
7479
|
+
event.data?.authoritativeRoutineObserved === true
|
|
7480
|
+
),
|
|
7481
|
+
"nonzero authoritative routine snapshot was not retained as observed evidence"
|
|
7482
|
+
);
|
|
7483
|
+
assert(
|
|
7484
|
+
!authoritativeRoutineRun.events.some(
|
|
7485
|
+
(event) =>
|
|
7486
|
+
event.type === "tool.started" &&
|
|
7487
|
+
/(?:^|[ /])(?:\\.private|private|raw|sqlite|jsonl)(?:$|[ /.-])/i.test(
|
|
7488
|
+
String(event.data?.args?.command || "")
|
|
7489
|
+
)
|
|
7490
|
+
),
|
|
7491
|
+
"authoritative routine flow still dispatched private/raw exploratory commands"
|
|
7492
|
+
);
|
|
7493
|
+
|
|
7291
7494
|
const hiddenDryRun = await runToolContractCase({
|
|
7292
7495
|
id: "native-hidden-dry-run",
|
|
7293
7496
|
goal: "Create hidden-dry-run.txt containing unsafe if this tool dispatches.",
|
|
@@ -7647,6 +7850,7 @@ console.log(
|
|
|
7647
7850
|
"finish-contract",
|
|
7648
7851
|
"per-turn-contract-preserved",
|
|
7649
7852
|
"schema-required-type-enum-extra",
|
|
7853
|
+
"authoritative-readonly-routine-first",
|
|
7650
7854
|
"native-hidden-dry-run-zero-dispatch",
|
|
7651
7855
|
"text-fallback-unoffered-zero-dispatch",
|
|
7652
7856
|
"strict-single-call-batch",
|
|
@@ -637,11 +637,23 @@ assert.equal(
|
|
|
637
637
|
"an incidental safety-clause 'cannot' was misclassified as a current external blocker"
|
|
638
638
|
);
|
|
639
639
|
assert.equal(finishResultClaimsIncompleteWork("Completed and verified the requested report."), false);
|
|
640
|
+
assert.equal(
|
|
641
|
+
finishResultClaimsIncompleteWork(
|
|
642
|
+
"Read-only status: Still retrying: external_pdf (quality_retry_pending; next attempt at 10:14). Nothing was sent or changed."
|
|
643
|
+
),
|
|
644
|
+
false,
|
|
645
|
+
"an external retry status was misclassified as unfinished agent work"
|
|
646
|
+
);
|
|
640
647
|
assert.equal(
|
|
641
648
|
finishResultClaimsIncompleteWork("The task is paused. A corrected implementation will be written next."),
|
|
642
649
|
true,
|
|
643
650
|
"future work was accepted as a completed result"
|
|
644
651
|
);
|
|
652
|
+
assert.equal(
|
|
653
|
+
finishResultClaimsIncompleteWork("The requested report is pending and I will complete it next."),
|
|
654
|
+
true,
|
|
655
|
+
"agent-owned pending report work was accepted as complete"
|
|
656
|
+
);
|
|
645
657
|
|
|
646
658
|
const outputFilenameContract = deriveScsTaskContract({
|
|
647
659
|
goal: "Save the complete cited report as `CLAIM_LEVEL_CITATION_RESEARCH.md` and include `negative evidence` in the report.",
|
package/scripts/smoke-skills.js
CHANGED
|
@@ -132,6 +132,12 @@ for (const unrelated of [
|
|
|
132
132
|
}
|
|
133
133
|
assert(selectedIds("debug Docker deployment logs and port config").includes("devops-deployment"), "devops prompt did not select devops-deployment");
|
|
134
134
|
assert(selectedIds("review auth security and secrets handling").includes("security-review"), "security prompt did not select security-review");
|
|
135
|
+
const securitySkill = skills.find((skill) => skill.id === "security-review");
|
|
136
|
+
assert(
|
|
137
|
+
securitySkill?.body.includes("carriage-return, newline") &&
|
|
138
|
+
securitySkill?.body.includes("residual risks and non-goals"),
|
|
139
|
+
"security skill omitted audit-record injection or structured security-note guidance"
|
|
140
|
+
);
|
|
135
141
|
assert(selectedIds("make a PowerPoint pitch deck").includes("presentation-slides"), "slides prompt did not select presentation-slides");
|
|
136
142
|
const presentationSkill = skills.find((skill) => skill.id === "presentation-slides");
|
|
137
143
|
assert(presentationSkill?.body.includes("Render every slide"), "presentation skill does not require every slide to be rendered");
|