sneakoscope 7.3.0 → 7.5.0
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 +117 -5
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/cli/codex-app-command.js +1 -1
- package/dist/cli/command-manifest-lite.js +2 -1
- package/dist/cli/install-helpers-codex-lb-selftest.js +2 -1
- package/dist/cli/install-helpers-install-support.js +6 -16
- package/dist/cli/install-helpers.js +59 -50
- package/dist/cli/router.js +9 -1
- package/dist/config/skills-manifest.json +50 -71
- package/dist/core/codex-app.js +5 -5
- package/dist/core/codex-native/core-skill-manifest.js +9 -6
- package/dist/core/commands/image-ux-review-command.js +243 -50
- package/dist/core/commands/naruto-command.js +245 -8
- package/dist/core/commands/ppt-command.js +2 -2
- package/dist/core/commands/telegram-command.js +123 -10
- package/dist/core/commands/triwiki-command.js +11 -1
- package/dist/core/commands/triwiki-graph-command.js +160 -0
- package/dist/core/commands/wiki-command.js +100 -20
- package/dist/core/errors/message.js +3 -0
- package/dist/core/feature-registry.js +2 -2
- package/dist/core/fsx.js +7 -3
- package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
- package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
- package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
- package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
- package/dist/core/hooks-runtime.js +28 -86
- package/dist/core/image-ux-review/callout-extraction.js +6 -0
- package/dist/core/image-ux-review/imagegen-adapter.js +54 -18
- package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
- package/dist/core/image-ux-review.js +182 -7
- package/dist/core/imagegen/codex-lb-imagegen-target.js +22 -7
- package/dist/core/imagegen/imagegen-capability.js +11 -4
- package/dist/core/imagegen/require-imagegen.js +29 -5
- package/dist/core/init/skills.js +22 -10
- package/dist/core/install/installed-package-smoke.js +157 -4
- package/dist/core/managed-path-safety.js +8 -10
- package/dist/core/mcp-config/backup.js +2 -4
- package/dist/core/mcp-config/config-reader.js +2 -4
- package/dist/core/mcp-config/scope.js +3 -5
- package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
- package/dist/core/naruto/context-graph-advisor.js +0 -0
- package/dist/core/pipeline-internals/runtime-core.js +44 -0
- package/dist/core/policy/latest-version-guidance.js +113 -0
- package/dist/core/ppt.js +8 -8
- package/dist/core/release/gate-manifest.js +32 -1
- package/dist/core/release/npm-stage-contract.js +21 -0
- package/dist/core/release/package-size-budget.js +2 -2
- package/dist/core/release/release-gate-contract.js +5 -0
- package/dist/core/release/stage-publish.js +27 -0
- package/dist/core/responses-stream.js +65 -19
- package/dist/core/routes/design-policy.js +7 -2
- package/dist/core/routes/dollar-manifest-lite.js +0 -4
- package/dist/core/routes/evidence.js +1 -1
- package/dist/core/routes.js +12 -8
- package/dist/core/safety/command-contract/contracts.js +2 -0
- package/dist/core/safety/command-contract/types.js +1 -1
- package/dist/core/search/context-graph-seeds.js +205 -0
- package/dist/core/search/context.js +0 -0
- package/dist/core/search/provider.js +2 -2
- package/dist/core/subagents/naruto-help-contract.js +2 -1
- package/dist/core/subagents/naruto-host-credentials.js +161 -0
- package/dist/core/subagents/official-subagent-preparation.js +4 -1
- package/dist/core/subagents/official-subagent-prompt.js +44 -2
- package/dist/core/subagents/official-subagent-runner.js +26 -8
- package/dist/core/subagents/triwiki-attention.js +135 -123
- package/dist/core/telegram/bot-api.js +59 -4
- package/dist/core/telegram/config.js +3 -2
- package/dist/core/telegram/hub.js +3 -2
- package/dist/core/telegram/owner-lock.js +4 -6
- package/dist/core/telegram/setup.js +223 -73
- package/dist/core/triwiki/code-pack.js +12 -120
- package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
- package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
- package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
- package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
- package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
- package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
- package/dist/core/triwiki/context-graph/contracts.js +236 -0
- package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
- package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
- package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
- package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
- package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
- package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
- package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
- package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
- package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
- package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
- package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
- package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
- package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
- package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
- package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
- package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
- package/dist/core/triwiki/context-graph/graph-index.js +143 -0
- package/dist/core/triwiki/context-graph/ids.js +0 -0
- package/dist/core/triwiki/context-graph/lint/index.js +0 -0
- package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
- package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
- package/dist/core/triwiki/context-graph/paths.js +116 -0
- package/dist/core/triwiki/context-graph/profiles.js +78 -0
- package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
- package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
- package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
- package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
- package/dist/core/triwiki/context-graph/projections/index.js +7 -0
- package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
- package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
- package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
- package/dist/core/triwiki/context-graph/query/explain.js +0 -0
- package/dist/core/triwiki/context-graph/query/index.js +178 -0
- package/dist/core/triwiki/context-graph/query/load.js +116 -0
- package/dist/core/triwiki/context-graph/query/pack.js +117 -0
- package/dist/core/triwiki/context-graph/query/rank.js +142 -0
- package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
- package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
- package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
- package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
- package/dist/core/triwiki/context-graph/query-types.js +29 -0
- package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
- package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
- package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
- package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
- package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
- package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
- package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
- package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
- package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
- package/dist/core/verification/context-graph-affected.js +271 -0
- package/dist/core/version.js +1 -1
- package/dist/scripts/context-graph-check.js +191 -0
- package/dist/scripts/helper-dedup-check.js +0 -1
- package/dist/scripts/installed-package-smoke-check.js +4 -1
- package/dist/scripts/latest-version-guidance-check.js +74 -0
- package/dist/scripts/release-metadata-check.js +20 -3
- package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
- package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
- package/package.json +5 -3
- package/release-gates.v2.json +174 -0
- package/schemas/triwiki/context-graph.schema.json +119 -0
- package/dist/core/triwiki/code-index-scanner.js +0 -309
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { flag, readOption } from '../../cli/args.js';
|
|
2
|
+
import { CONTEXT_GRAPH_REPAIR_COMMAND } from '../triwiki/context-graph/contracts.js';
|
|
3
|
+
import { contextGraphFreshnessPreflight } from '../hooks-runtime/context-graph-freshness-preflight.js';
|
|
4
|
+
import { contextGraphExtractors } from '../triwiki/context-graph/extractors/index.js';
|
|
5
|
+
import { runContextGraphLint } from '../triwiki/context-graph/lint/index.js';
|
|
6
|
+
import { contextGraphQueryProfile } from '../triwiki/context-graph/profiles.js';
|
|
7
|
+
import { queryContextGraph } from '../triwiki/context-graph/query/index.js';
|
|
8
|
+
import { contextGraphStatus } from '../triwiki/context-graph/store/graph-status.js';
|
|
9
|
+
import { readContextGraphMeta, readContextGraphSnapshot } from '../triwiki/context-graph/store/snapshot-store.js';
|
|
10
|
+
export const TRIWIKI_GRAPH_SUBCOMMANDS = ['graph-status', 'graph-lint', 'graph-query'];
|
|
11
|
+
export function isTriWikiGraphSubcommand(value) {
|
|
12
|
+
return typeof value === 'string' && TRIWIKI_GRAPH_SUBCOMMANDS.includes(value);
|
|
13
|
+
}
|
|
14
|
+
function issueLine(issue) {
|
|
15
|
+
const where = issue.nodeId ?? issue.edgeId ?? issue.path ?? '';
|
|
16
|
+
return `${issue.severity}: ${issue.code}${where ? ` ${where}` : ''} — ${issue.message}`;
|
|
17
|
+
}
|
|
18
|
+
async function graphStatusFast(root) {
|
|
19
|
+
const preflight = await contextGraphFreshnessPreflight(root);
|
|
20
|
+
return {
|
|
21
|
+
result: { ...preflight, mode: 'fast', ok: preflight.usable },
|
|
22
|
+
ok: preflight.usable,
|
|
23
|
+
lines: [
|
|
24
|
+
`Context graph: ${preflight.status} (${preflight.node_count} nodes, ${preflight.edge_count} edges, ${preflight.coverage})`,
|
|
25
|
+
...(preflight.reasons.length ? [`Stale reasons: ${preflight.reasons.join(', ')}`] : []),
|
|
26
|
+
...(preflight.unverified_reasons.length ? [`Not evaluated: ${preflight.unverified_reasons.join(', ')}`] : []),
|
|
27
|
+
...(preflight.usable ? [] : [`Repair: ${preflight.repair_command}`])
|
|
28
|
+
]
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
async function graphStatus(root) {
|
|
32
|
+
const status = await contextGraphStatus(root, { extractors: contextGraphExtractors() });
|
|
33
|
+
const meta = await readContextGraphMeta(root);
|
|
34
|
+
const result = {
|
|
35
|
+
schema: 'sks.triwiki-graph-status.v1',
|
|
36
|
+
ok: status.status === 'fresh',
|
|
37
|
+
status: status.status,
|
|
38
|
+
snapshot_hash: status.snapshotHash,
|
|
39
|
+
generated_at: status.generatedAt,
|
|
40
|
+
stale_reasons: status.reasons,
|
|
41
|
+
error_code: status.errorCode,
|
|
42
|
+
node_count: status.nodeCount,
|
|
43
|
+
edge_count: status.edgeCount,
|
|
44
|
+
lint: meta.status === 'ok' && meta.meta ? meta.meta.lint : null,
|
|
45
|
+
repair_command: status.repairCommand
|
|
46
|
+
};
|
|
47
|
+
const lines = [
|
|
48
|
+
`Context graph: ${status.status} (${status.nodeCount} nodes, ${status.edgeCount} edges)`,
|
|
49
|
+
...(status.snapshotHash ? [`Snapshot: ${status.snapshotHash.slice(0, 16)}`] : []),
|
|
50
|
+
...(status.reasons.length ? [`Stale reasons: ${status.reasons.join(', ')}`] : []),
|
|
51
|
+
...(status.status === 'fresh' ? [] : [`Repair: ${status.repairCommand}`])
|
|
52
|
+
];
|
|
53
|
+
return { result, ok: result.ok, lines };
|
|
54
|
+
}
|
|
55
|
+
async function graphLint(root) {
|
|
56
|
+
const snapshotLoad = await readContextGraphSnapshot(root);
|
|
57
|
+
if (snapshotLoad.status !== 'ok' || !snapshotLoad.snapshot) {
|
|
58
|
+
const errorCode = snapshotLoad.status === 'missing' ? 'context_graph_missing' : 'context_graph_corrupt';
|
|
59
|
+
return {
|
|
60
|
+
result: {
|
|
61
|
+
schema: 'sks.triwiki-graph-lint.v1',
|
|
62
|
+
ok: false,
|
|
63
|
+
error_code: errorCode,
|
|
64
|
+
errors: [],
|
|
65
|
+
warnings: [],
|
|
66
|
+
repair_command: CONTEXT_GRAPH_REPAIR_COMMAND
|
|
67
|
+
},
|
|
68
|
+
ok: false,
|
|
69
|
+
lines: [`Context graph lint: ${errorCode}`, `Repair: ${CONTEXT_GRAPH_REPAIR_COMMAND}`]
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const meta = await readContextGraphMeta(root);
|
|
73
|
+
const lint = runContextGraphLint({
|
|
74
|
+
root,
|
|
75
|
+
snapshot: snapshotLoad.snapshot,
|
|
76
|
+
meta: meta.status === 'ok' ? meta.meta : null
|
|
77
|
+
});
|
|
78
|
+
const result = {
|
|
79
|
+
schema: 'sks.triwiki-graph-lint.v1',
|
|
80
|
+
ok: lint.ok,
|
|
81
|
+
snapshot_hash: snapshotLoad.snapshot.snapshotHash,
|
|
82
|
+
error_count: lint.errorCount,
|
|
83
|
+
warning_count: lint.warningCount,
|
|
84
|
+
errors: lint.errors.map(issueLine),
|
|
85
|
+
warnings: lint.warnings.slice(0, 40).map(issueLine),
|
|
86
|
+
repair_command: CONTEXT_GRAPH_REPAIR_COMMAND
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
result,
|
|
90
|
+
ok: lint.ok,
|
|
91
|
+
lines: [
|
|
92
|
+
`Context graph lint: ${lint.ok ? 'ok' : 'blocked'} (${lint.errorCount} errors, ${lint.warningCount} warnings)`,
|
|
93
|
+
...lint.errors.slice(0, 20).map((issue) => `- ${issueLine(issue)}`)
|
|
94
|
+
]
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async function graphQuery(root, args) {
|
|
98
|
+
const query = args.find((arg) => !arg.startsWith('-')) ?? '';
|
|
99
|
+
const profile = contextGraphQueryProfile(readOption(args, '--profile', 'implementation')).name;
|
|
100
|
+
const budgetRaw = readOption(args, '--token-budget', '');
|
|
101
|
+
const parsedBudget = budgetRaw ? Number.parseInt(String(budgetRaw), 10) : Number.NaN;
|
|
102
|
+
if (!query) {
|
|
103
|
+
return {
|
|
104
|
+
result: { schema: 'sks.triwiki-graph-query.v1', ok: false, errors: ['missing_query'] },
|
|
105
|
+
ok: false,
|
|
106
|
+
lines: ['Usage: sks triwiki graph-query "<query>" [--profile implementation|review|planning|answer] [--json]']
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const answer = await queryContextGraph({
|
|
110
|
+
root,
|
|
111
|
+
query,
|
|
112
|
+
profile,
|
|
113
|
+
...(Number.isFinite(parsedBudget) ? { tokenBudget: parsedBudget } : {})
|
|
114
|
+
});
|
|
115
|
+
const result = {
|
|
116
|
+
schema: 'sks.triwiki-graph-query.v1',
|
|
117
|
+
ok: answer.ok,
|
|
118
|
+
profile: answer.profile,
|
|
119
|
+
snapshot_hash: answer.snapshotHash,
|
|
120
|
+
snapshot_freshness: answer.snapshotFreshness,
|
|
121
|
+
seed_count: answer.seedCount,
|
|
122
|
+
visited_nodes: answer.visitedNodes,
|
|
123
|
+
selected: answer.selected.map((node) => ({
|
|
124
|
+
id: node.nodeId,
|
|
125
|
+
kind: node.kind,
|
|
126
|
+
path: node.path ?? null,
|
|
127
|
+
score: node.score,
|
|
128
|
+
trust: node.trust,
|
|
129
|
+
freshness: node.freshness,
|
|
130
|
+
token_cost: node.tokenCost,
|
|
131
|
+
reason_path: node.reasonPath,
|
|
132
|
+
provenance: node.provenance
|
|
133
|
+
})),
|
|
134
|
+
provenance_coverage: answer.provenanceCoverage,
|
|
135
|
+
stale_excluded: answer.staleExcluded,
|
|
136
|
+
invalidated_excluded: answer.invalidatedExcluded,
|
|
137
|
+
token_cost: answer.tokenCost,
|
|
138
|
+
token_budget: answer.tokenBudget,
|
|
139
|
+
truncated: answer.truncated,
|
|
140
|
+
timeout: answer.timeout,
|
|
141
|
+
omission_reasons: answer.omissionReasons,
|
|
142
|
+
warnings: answer.warnings,
|
|
143
|
+
errors: answer.errors,
|
|
144
|
+
repair_command: CONTEXT_GRAPH_REPAIR_COMMAND
|
|
145
|
+
};
|
|
146
|
+
const lines = answer.ok
|
|
147
|
+
? [
|
|
148
|
+
`Context graph query (${answer.profile}): ${answer.selected.length} nodes, ${answer.tokenCost}/${answer.tokenBudget} tokens`,
|
|
149
|
+
...answer.selected.slice(0, 12).map((node) => `- ${node.nodeId} [${node.reasonPath.join(' ')}]`)
|
|
150
|
+
]
|
|
151
|
+
: [`Context graph query blocked: ${answer.errors.join(', ')}`, `Repair: ${CONTEXT_GRAPH_REPAIR_COMMAND}`];
|
|
152
|
+
return { result, ok: answer.ok, lines };
|
|
153
|
+
}
|
|
154
|
+
export async function triwikiGraphCommand(root, sub, args) {
|
|
155
|
+
if (sub === 'graph-status')
|
|
156
|
+
return flag(args, '--fast') ? graphStatusFast(root) : graphStatus(root);
|
|
157
|
+
if (sub === 'graph-lint')
|
|
158
|
+
return graphLint(root);
|
|
159
|
+
return graphQuery(root, args.filter((arg) => arg !== 'graph-query' && !flag([arg], '--json')));
|
|
160
|
+
}
|
|
@@ -20,8 +20,15 @@ import { wrongnessContextForRoute } from '../triwiki-wrongness/wrongness-retriev
|
|
|
20
20
|
import { readCombinedWrongnessRecords } from '../triwiki-wrongness/wrongness-ledger.js';
|
|
21
21
|
import { recordImageWrongnessFromValidation } from '../triwiki-wrongness/image-wrongness.js';
|
|
22
22
|
import { publishSharedMemory, rebuildSharedIndexes, sharedMemorySummary, validateSharedMemory } from '../git-hygiene/shared-memory-publish.js';
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
23
|
+
import { codePackPath, isCodePackProjectionBoundToSnapshot, validateCodePack, writeCodePackAtomic } from '../triwiki/code-pack.js';
|
|
24
|
+
import { repairTriWikiProofIndex } from '../triwiki/triwiki-proof-bank-index.js';
|
|
25
|
+
import { runContextGraphLint } from '../triwiki/context-graph/lint/index.js';
|
|
26
|
+
import { contextGraphStatus } from '../triwiki/context-graph/store/graph-status.js';
|
|
27
|
+
import { readContextGraphMeta, readContextGraphSnapshot } from '../triwiki/context-graph/store/snapshot-store.js';
|
|
28
|
+
import { compileContextGraph } from '../triwiki/context-graph/compiler/index.js';
|
|
29
|
+
import { contextGraphExtractors } from '../triwiki/context-graph/extractors/index.js';
|
|
30
|
+
import { buildWorkspaceCodePack } from '../triwiki/context-graph/projections/code-pack-workspace.js';
|
|
31
|
+
import { CONTEXT_GRAPH_REPAIR_COMMAND } from '../triwiki/context-graph/contracts.js';
|
|
25
32
|
import { inspectCodePackHeadFreshness } from '../triwiki/code-pack-head-freshness.js';
|
|
26
33
|
import { sealTriWikiContextPack, validateTriWikiContextPackProvenance } from '../triwiki-provenance.js';
|
|
27
34
|
import { flag, positionalArgs, readFlagValue, readOption, resolveMissionId } from './command-utils.js';
|
|
@@ -205,46 +212,119 @@ export async function wikiCommand(sub, args = []) {
|
|
|
205
212
|
const pack = await readJson(path.resolve(target));
|
|
206
213
|
const { result, trustAnchors } = wikiValidationResult(pack, root);
|
|
207
214
|
const codePack = await codePackFreshness(root);
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
215
|
+
const graph = await wikiValidateContextGraph(root);
|
|
216
|
+
const ok = result.ok && graph.ok;
|
|
217
|
+
process.exitCode = ok ? 0 : 2;
|
|
218
|
+
if (flag(args, '--json')) {
|
|
219
|
+
return console.log(JSON.stringify({ ...result, ok, code_pack: codePack, context_graph: graph }, null, 2));
|
|
220
|
+
}
|
|
211
221
|
console.log(`Wiki coordinate index: ${result.ok ? 'ok' : 'failed'}`);
|
|
212
222
|
console.log(`Anchors checked: ${result.checked}`);
|
|
213
223
|
console.log(`Trust anchors: ${trustAnchors}/${result.checked}`);
|
|
214
224
|
for (const issue of result.issues)
|
|
215
225
|
console.log(`- ${issue.severity}: ${issue.id}${issue.anchor ? ` ${issue.anchor}` : ''}`);
|
|
216
|
-
console.log(`Code pack: ${codePack.status}${codePack.status === 'stale' ?
|
|
226
|
+
console.log(`Code pack: ${codePack.status}${codePack.status === 'stale' ? ` — run \`${CONTEXT_GRAPH_REPAIR_COMMAND}\`` : ''}`);
|
|
227
|
+
console.log(`Context graph: ${graph.status}${graph.ok ? '' : ` — run \`${CONTEXT_GRAPH_REPAIR_COMMAND}\``}`);
|
|
228
|
+
for (const issue of graph.issues)
|
|
229
|
+
console.log(`- ${issue}`);
|
|
217
230
|
return;
|
|
218
231
|
}
|
|
219
232
|
console.error('Usage: sks wiki coords|pack|refresh|publish|rebuild-index|rebuild-summary|validate|validate-shared|wrongness|image-ingest|anchor-add|relation-add|image-validate|image-summary');
|
|
220
233
|
process.exitCode = 1;
|
|
221
234
|
}
|
|
235
|
+
async function wikiValidateContextGraph(root) {
|
|
236
|
+
const status = await contextGraphStatus(root, { extractors: contextGraphExtractors() });
|
|
237
|
+
if (status.status !== 'fresh') {
|
|
238
|
+
return {
|
|
239
|
+
ok: false,
|
|
240
|
+
status: status.status,
|
|
241
|
+
snapshot_hash: status.snapshotHash,
|
|
242
|
+
issues: [status.errorCode ?? 'context_graph_not_fresh', ...status.reasons]
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
const snapshotLoad = await readContextGraphSnapshot(root);
|
|
246
|
+
const metaLoad = await readContextGraphMeta(root);
|
|
247
|
+
if (snapshotLoad.status !== 'ok' || !snapshotLoad.snapshot || metaLoad.status !== 'ok' || !metaLoad.meta) {
|
|
248
|
+
return { ok: false, status: 'corrupt', snapshot_hash: status.snapshotHash, issues: ['context_graph_snapshot_meta_unreadable'] };
|
|
249
|
+
}
|
|
250
|
+
const lint = runContextGraphLint({ root, snapshot: snapshotLoad.snapshot, meta: metaLoad.meta });
|
|
251
|
+
const issues = lint.errors.slice(0, 20).map((issue) => `${issue.code}: ${issue.message}`);
|
|
252
|
+
const pack = await readJson(codePackPath(root), null).catch(() => null);
|
|
253
|
+
const digest = typeof pack?.index_digest === 'string' ? pack.index_digest : '';
|
|
254
|
+
const boundToSnapshot = Boolean(pack
|
|
255
|
+
&& Array.isArray(pack.entries)
|
|
256
|
+
&& isCodePackProjectionBoundToSnapshot(snapshotLoad.snapshot.snapshotHash, pack));
|
|
257
|
+
if (digest && !boundToSnapshot)
|
|
258
|
+
issues.push('code_pack_not_bound_to_current_snapshot');
|
|
259
|
+
return {
|
|
260
|
+
ok: lint.ok && (!digest || boundToSnapshot),
|
|
261
|
+
status: 'fresh',
|
|
262
|
+
snapshot_hash: snapshotLoad.snapshot.snapshotHash,
|
|
263
|
+
issues
|
|
264
|
+
};
|
|
265
|
+
}
|
|
222
266
|
async function wikiRefreshCode(args = []) {
|
|
223
267
|
const root = await sksRoot();
|
|
224
|
-
const index = await scanCodebaseIndex(root);
|
|
225
268
|
const tokenBudgetRaw = readFlagValue(args, '--token-budget', null);
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
const
|
|
269
|
+
const parsedBudget = tokenBudgetRaw ? Number.parseInt(String(tokenBudgetRaw), 10) : Number.NaN;
|
|
270
|
+
const tokenBudget = Number.isFinite(parsedBudget) ? parsedBudget : undefined;
|
|
271
|
+
const proofIndex = repairTriWikiProofIndex(root);
|
|
272
|
+
const compiled = await compileContextGraph({
|
|
273
|
+
root,
|
|
274
|
+
extractors: contextGraphExtractors(),
|
|
275
|
+
useFragmentCache: false
|
|
276
|
+
});
|
|
277
|
+
const lintErrors = compiled.issues.filter((issue) => issue.severity === 'error');
|
|
278
|
+
if (!compiled.ok || !compiled.snapshot) {
|
|
279
|
+
const blocked = {
|
|
280
|
+
schema: 'sks.wiki-refresh-code.v1',
|
|
281
|
+
ok: false,
|
|
282
|
+
reason: compiled.reason ?? 'context_graph_compile_failed',
|
|
283
|
+
blockers: compiled.blockers ?? [],
|
|
284
|
+
issues: lintErrors.slice(0, 20).map((issue) => `${issue.code}: ${issue.message}`),
|
|
285
|
+
repair_command: CONTEXT_GRAPH_REPAIR_COMMAND,
|
|
286
|
+
written: null
|
|
287
|
+
};
|
|
288
|
+
process.exitCode = 2;
|
|
289
|
+
process.stderr.write(`wiki-refresh-code: root=${root} graph=blocked reason=${blocked.reason} blockers=${JSON.stringify(blocked.blockers)}\n`);
|
|
290
|
+
if (flag(args, '--json'))
|
|
291
|
+
return console.log(JSON.stringify(blocked, null, 2));
|
|
292
|
+
console.log('Sneakoscope TriWiki Context Graph Refresh');
|
|
293
|
+
console.log(`Context graph: blocked (${blocked.reason})`);
|
|
294
|
+
for (const issue of blocked.issues)
|
|
295
|
+
console.log(`- ${issue}`);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const projected = await buildWorkspaceCodePack(root, {
|
|
299
|
+
...(tokenBudget === undefined ? {} : { tokenBudget }),
|
|
300
|
+
status: { status: 'fresh', snapshotHash: compiled.snapshotHash }
|
|
301
|
+
});
|
|
302
|
+
const pack = projected.pack;
|
|
303
|
+
const validation = pack ? await validateCodePack(pack, root) : { ok: false, issues: projected.errors };
|
|
304
|
+
const written = pack && validation.ok ? await writeCodePackAtomic(root, pack) : null;
|
|
230
305
|
const result = {
|
|
231
306
|
schema: 'sks.wiki-refresh-code.v1',
|
|
232
307
|
ok: validation.ok,
|
|
233
308
|
issues: validation.issues,
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
309
|
+
snapshot_hash: compiled.snapshotHash,
|
|
310
|
+
nodes: compiled.snapshot.nodeCount,
|
|
311
|
+
edges: compiled.snapshot.edgeCount,
|
|
312
|
+
graph_warnings: compiled.issues.filter((issue) => issue.severity === 'warning').length,
|
|
313
|
+
incremental: Boolean(compiled.incremental),
|
|
314
|
+
proof_index: { ok: proofIndex.ok, indexed: proofIndex.indexed_count, previous_status: proofIndex.previous_status },
|
|
315
|
+
compile_ms: compiled.durationMs,
|
|
316
|
+
entries: pack ? pack.entries.length : 0,
|
|
317
|
+
token_cost: pack ? pack.total_token_cost : 0,
|
|
318
|
+
token_budget: pack ? pack.token_budget : 0,
|
|
240
319
|
written: written ? written.path : null
|
|
241
320
|
};
|
|
242
321
|
process.exitCode = validation.ok ? 0 : 2;
|
|
243
|
-
process.stderr.write(`wiki-refresh-code: root=${root}
|
|
322
|
+
process.stderr.write(`wiki-refresh-code: root=${root} nodes=${result.nodes} edges=${result.edges} entries=${result.entries} token_cost=${result.token_cost}/${result.token_budget} issues=${JSON.stringify(validation.issues)}\n`);
|
|
244
323
|
if (flag(args, '--json'))
|
|
245
324
|
return console.log(JSON.stringify(result, null, 2));
|
|
246
|
-
console.log('Sneakoscope TriWiki
|
|
247
|
-
console.log(`
|
|
325
|
+
console.log('Sneakoscope TriWiki Context Graph Refresh');
|
|
326
|
+
console.log(`Context graph: ok (${result.nodes} nodes, ${result.edges} edges, snapshot ${String(result.snapshot_hash).slice(0, 12)})`);
|
|
327
|
+
console.log(`Code pack projection: ${validation.ok ? 'ok' : 'blocked'} (${result.entries} entries, ${result.token_cost}/${result.token_budget} tokens)`);
|
|
248
328
|
for (const issue of validation.issues)
|
|
249
329
|
console.log(`- ${issue}`);
|
|
250
330
|
}
|
|
@@ -735,8 +735,8 @@ function imagegenWiringFeatures() {
|
|
|
735
735
|
cli_command_names: ['image-ux-review'],
|
|
736
736
|
handler_keys: ['image-ux-review', 'ux-review'],
|
|
737
737
|
dollar_commands: ['$Image-UX-Review', '$UX-Review'],
|
|
738
|
-
app_skill_aliases: ['$image-ux-review'
|
|
739
|
-
skills: ['image-ux-review', '
|
|
738
|
+
app_skill_aliases: ['$image-ux-review'],
|
|
739
|
+
skills: ['image-ux-review', 'imagegen']
|
|
740
740
|
}
|
|
741
741
|
}),
|
|
742
742
|
baseFeature({
|
package/dist/core/fsx.js
CHANGED
|
@@ -227,18 +227,22 @@ export async function writeReceiptRotated(p, data, opts = {}) {
|
|
|
227
227
|
.slice(keep);
|
|
228
228
|
await Promise.all(removable.map((row) => fsp.rm(row.file, { force: true }).catch(() => undefined)));
|
|
229
229
|
}
|
|
230
|
-
export async function writeBinaryAtomic(p, data) {
|
|
230
|
+
export async function writeBinaryAtomic(p, data, options = {}) {
|
|
231
231
|
await ensureDir(path.dirname(p));
|
|
232
232
|
try {
|
|
233
|
-
if ((await fsp.readFile(p)).equals(data))
|
|
233
|
+
if (options.mode === undefined && (await fsp.readFile(p)).equals(data))
|
|
234
234
|
return;
|
|
235
235
|
}
|
|
236
236
|
catch { }
|
|
237
237
|
const tmp = `${p}.${process.pid}.${randomId(6)}.tmp`;
|
|
238
238
|
try {
|
|
239
|
-
const handle =
|
|
239
|
+
const handle = options.mode === undefined
|
|
240
|
+
? await fsp.open(tmp, 'w')
|
|
241
|
+
: await fsp.open(tmp, 'w', options.mode);
|
|
240
242
|
try {
|
|
241
243
|
await handle.writeFile(data);
|
|
244
|
+
if (options.mode !== undefined)
|
|
245
|
+
await handle.chmod(options.mode);
|
|
242
246
|
await handle.sync().catch(() => { });
|
|
243
247
|
}
|
|
244
248
|
finally {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { packageRoot, runProcess, tmpdir } from '../fsx.js';
|
|
3
|
+
export async function selftestCodexCommitHooks() {
|
|
4
|
+
const root = tmpdir();
|
|
5
|
+
const hookBin = path.join(packageRoot(), 'dist', 'bin', 'sks.js');
|
|
6
|
+
const env = { SKS_DISABLE_UPDATE_CHECK: '1' };
|
|
7
|
+
const setup = await runProcess(process.execPath, [hookBin, 'setup', '--install-scope', 'project'], { cwd: root, env, timeoutMs: 15000, maxOutputBytes: 128 * 1024 });
|
|
8
|
+
if (setup.code !== 0)
|
|
9
|
+
throw new Error(`selftest failed: commit setup ${setup.code}: ${setup.stderr}`);
|
|
10
|
+
const runHook = (name, payload) => runProcess(process.execPath, [hookBin, 'hook', name], { cwd: root, input: JSON.stringify({ cwd: root, ...payload }), env, timeoutMs: 15000, maxOutputBytes: 128 * 1024 });
|
|
11
|
+
const id = 'commit-selftest';
|
|
12
|
+
const hook = await runHook('user-prompt-submit', { conversation_id: id, action: 'codex_git_commit', prompt: 'Generate a git commit message for the staged diff.' });
|
|
13
|
+
if (hook.code !== 0)
|
|
14
|
+
throw new Error(`selftest failed: commit hook ${hook.code}: ${hook.stderr}`);
|
|
15
|
+
const hookJson = JSON.parse(hook.stdout);
|
|
16
|
+
if (hookJson.decision === 'block' || hookJson.hookSpecificOutput?.additionalContext || !String(hookJson.systemMessage || '').includes('git action'))
|
|
17
|
+
throw new Error('selftest failed: commit route bypass');
|
|
18
|
+
const stop = await runHook('stop', { conversation_id: id, last_assistant_message: 'Fix Codex App commit message hook bypass' });
|
|
19
|
+
if (stop.code !== 0)
|
|
20
|
+
throw new Error(`selftest failed: commit stop ${stop.code}: ${stop.stderr}`);
|
|
21
|
+
const stopJson = JSON.parse(stop.stdout);
|
|
22
|
+
if (stopJson.decision === 'block' || !String(stopJson.systemMessage || '').includes('accepted without route finalization'))
|
|
23
|
+
throw new Error('selftest failed: commit stop bypass');
|
|
24
|
+
const commitPushId = 'commit-push-selftest';
|
|
25
|
+
const appCommitPushHook = await runHook('user-prompt-submit', { conversation_id: commitPushId, action: 'Codex App Git Actions Commit and Push', prompt: 'Commit and push changes.' });
|
|
26
|
+
if (appCommitPushHook.code !== 0)
|
|
27
|
+
throw new Error(`selftest failed: app commit-push hook ${appCommitPushHook.code}: ${appCommitPushHook.stderr}`);
|
|
28
|
+
const appCommitPushJson = JSON.parse(appCommitPushHook.stdout);
|
|
29
|
+
if (appCommitPushJson.decision === 'block' || appCommitPushJson.hookSpecificOutput?.additionalContext || !String(appCommitPushJson.systemMessage || '').includes('git action'))
|
|
30
|
+
throw new Error('selftest failed: app commit-push route bypass');
|
|
31
|
+
const appCommitPushStop = await runHook('stop', { conversation_id: commitPushId, last_assistant_message: 'Commit and push complete.' });
|
|
32
|
+
if (appCommitPushStop.code !== 0)
|
|
33
|
+
throw new Error(`selftest failed: app commit-push stop ${appCommitPushStop.code}: ${appCommitPushStop.stderr}`);
|
|
34
|
+
if (JSON.parse(appCommitPushStop.stdout).decision === 'block')
|
|
35
|
+
throw new Error('selftest failed: app commit-push stop bypass');
|
|
36
|
+
const appPushId = 'app-push-selftest';
|
|
37
|
+
const appPushHook = await runHook('user-prompt-submit', { conversation_id: appPushId, metadata: { source: 'codex_app', action: 'Git Actions Push' }, prompt: 'Push changes.' });
|
|
38
|
+
if (appPushHook.code !== 0)
|
|
39
|
+
throw new Error(`selftest failed: app push hook ${appPushHook.code}: ${appPushHook.stderr}`);
|
|
40
|
+
const appPushJson = JSON.parse(appPushHook.stdout);
|
|
41
|
+
if (appPushJson.decision === 'block' || appPushJson.hookSpecificOutput?.additionalContext || !String(appPushJson.systemMessage || '').includes('git action'))
|
|
42
|
+
throw new Error('selftest failed: app push metadata route bypass');
|
|
43
|
+
const appPushStop = await runHook('stop', { conversation_id: appPushId, metadata: { source: 'codex_app', action: 'Git Actions Push' }, last_assistant_message: 'Done.' });
|
|
44
|
+
if (appPushStop.code !== 0)
|
|
45
|
+
throw new Error(`selftest failed: app push stop ${appPushStop.code}: ${appPushStop.stderr}`);
|
|
46
|
+
if (JSON.parse(appPushStop.stdout).decision === 'block')
|
|
47
|
+
throw new Error('selftest failed: app push metadata stop bypass');
|
|
48
|
+
const metadataLightId = 'metadata-light-commit-push-selftest';
|
|
49
|
+
const metadataLightHook = await runHook('user-prompt-submit', { conversation_id: metadataLightId, prompt: 'Commit and push changes.' });
|
|
50
|
+
if (metadataLightHook.code !== 0)
|
|
51
|
+
throw new Error(`selftest failed: metadata-light commit-push hook ${metadataLightHook.code}: ${metadataLightHook.stderr}`);
|
|
52
|
+
const metadataLightJson = JSON.parse(metadataLightHook.stdout);
|
|
53
|
+
if (metadataLightJson.decision === 'block' || metadataLightJson.hookSpecificOutput?.additionalContext || !String(metadataLightJson.systemMessage || '').includes('git action'))
|
|
54
|
+
throw new Error('selftest failed: metadata-light app commit-push route bypass');
|
|
55
|
+
const metadataLightStop = await runHook('stop', { conversation_id: metadataLightId, last_assistant_message: 'Commit and push complete.' });
|
|
56
|
+
if (metadataLightStop.code !== 0)
|
|
57
|
+
throw new Error(`selftest failed: metadata-light commit-push stop ${metadataLightStop.code}: ${metadataLightStop.stderr}`);
|
|
58
|
+
if (JSON.parse(metadataLightStop.stdout).decision === 'block')
|
|
59
|
+
throw new Error('selftest failed: metadata-light commit-push stop bypass');
|
|
60
|
+
const settingsHook = await runHook('user-prompt-submit', { model: 'future-codex-catalog-model', metadata: { source: 'codex_app_settings', feature: 'speed profile' } });
|
|
61
|
+
if (settingsHook.code !== 0)
|
|
62
|
+
throw new Error(`selftest failed: settings hook ${settingsHook.code}: ${settingsHook.stderr}`);
|
|
63
|
+
const settingsJson = JSON.parse(settingsHook.stdout);
|
|
64
|
+
if (settingsJson.decision === 'block' || settingsJson.hookSpecificOutput?.additionalContext || !String(settingsJson.systemMessage || '').includes('settings/profile event ignored'))
|
|
65
|
+
throw new Error('selftest failed: settings/profile event should not route or block');
|
|
66
|
+
const userHook = await runHook('user-prompt-submit', { prompt: '[커밋 메시지를 생성하지 못했습니다.] 코덱스 앱에서 이 버그 수정해줘' });
|
|
67
|
+
if (userHook.code !== 0)
|
|
68
|
+
throw new Error(`selftest failed: user commit hook ${userHook.code}: ${userHook.stderr}`);
|
|
69
|
+
if (!JSON.parse(userHook.stdout).hookSpecificOutput?.additionalContext?.includes('$Naruto route prepared'))
|
|
70
|
+
throw new Error('selftest failed: user prompt route');
|
|
71
|
+
const userCommitPushHook = await runHook('user-prompt-submit', { prompt: '배포하게 커밋하고 푸쉬해줘' });
|
|
72
|
+
if (userCommitPushHook.code !== 0)
|
|
73
|
+
throw new Error(`selftest failed: user commit-push hook ${userCommitPushHook.code}: ${userCommitPushHook.stderr}`);
|
|
74
|
+
const userCommitPushJson = JSON.parse(userCommitPushHook.stdout);
|
|
75
|
+
if (userCommitPushJson.decision === 'block' || userCommitPushJson.hookSpecificOutput?.additionalContext || !String(userCommitPushJson.systemMessage || '').includes('git action'))
|
|
76
|
+
throw new Error('selftest failed: user commit-push prompt should bypass route');
|
|
77
|
+
const userCommitPushStop = await runHook('stop', { last_assistant_message: 'Commit and push complete.' });
|
|
78
|
+
if (userCommitPushStop.code !== 0)
|
|
79
|
+
throw new Error(`selftest failed: user commit-push stop ${userCommitPushStop.code}: ${userCommitPushStop.stderr}`);
|
|
80
|
+
if (JSON.parse(userCommitPushStop.stdout).decision === 'block')
|
|
81
|
+
throw new Error('selftest failed: user commit-push stop bypass');
|
|
82
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { CONTEXT_GRAPH_REPAIR_COMMAND } from '../triwiki/context-graph/contracts.js';
|
|
2
|
+
import { contextGraphCacheKey } from '../triwiki/context-graph/compiler/cache-key.js';
|
|
3
|
+
import { contextGraphStatus } from '../triwiki/context-graph/store/graph-status.js';
|
|
4
|
+
import { readContextGraphMeta } from '../triwiki/context-graph/store/snapshot-store.js';
|
|
5
|
+
import { contextGraphExtractors } from '../triwiki/context-graph/extractors/index.js';
|
|
6
|
+
export const CONTEXT_GRAPH_FRESHNESS_PREFLIGHT_SCHEMA = 'sks.context-graph-freshness-preflight.v1';
|
|
7
|
+
const CACHE_KEY_DERIVED_REASONS = [
|
|
8
|
+
'head_changed',
|
|
9
|
+
'dirty_fingerprint_changed',
|
|
10
|
+
'schema_revision_changed',
|
|
11
|
+
'tsconfig_changed',
|
|
12
|
+
'command_manifest_changed',
|
|
13
|
+
'gate_manifest_changed',
|
|
14
|
+
'proof_index_changed',
|
|
15
|
+
'wiki_context_changed',
|
|
16
|
+
'git_state_unknown',
|
|
17
|
+
'cache_key_changed'
|
|
18
|
+
];
|
|
19
|
+
const PLACEHOLDER_CACHE_KEY_PARTS = {
|
|
20
|
+
workspaceIdentity: 'unevaluated',
|
|
21
|
+
head: null,
|
|
22
|
+
gitState: 'unknown',
|
|
23
|
+
trackedDirtyFingerprint: 'unevaluated',
|
|
24
|
+
untrackedFingerprint: 'unevaluated',
|
|
25
|
+
schemaRevision: 'unevaluated',
|
|
26
|
+
tsconfigHash: 'unevaluated',
|
|
27
|
+
commandManifestHash: 'unevaluated',
|
|
28
|
+
gateManifestHash: 'unevaluated',
|
|
29
|
+
proofIndexHash: 'unevaluated',
|
|
30
|
+
wikiContextHash: 'unevaluated'
|
|
31
|
+
};
|
|
32
|
+
export async function contextGraphFreshnessPreflight(root, options = {}) {
|
|
33
|
+
const verifySources = options.verifySources !== false;
|
|
34
|
+
const extractors = options.extractors ?? contextGraphExtractors();
|
|
35
|
+
const supplied = options.cacheKey ?? null;
|
|
36
|
+
const metaLoad = await readContextGraphMeta(root);
|
|
37
|
+
const meta = metaLoad.status === 'ok' ? metaLoad.meta : null;
|
|
38
|
+
const cacheKey = supplied ?? neutralCacheKey(meta?.cacheKeyParts ?? PLACEHOLDER_CACHE_KEY_PARTS);
|
|
39
|
+
const recordedSources = meta ? Object.keys(meta.inputHashes ?? {}).length : 0;
|
|
40
|
+
const status = await contextGraphStatus(root, {
|
|
41
|
+
extractors,
|
|
42
|
+
cacheKey,
|
|
43
|
+
verifySources,
|
|
44
|
+
...(options.maxVerifiedSources === undefined ? {} : { maxVerifiedSources: options.maxVerifiedSources })
|
|
45
|
+
});
|
|
46
|
+
const sourcesVerified = verifySources
|
|
47
|
+
&& (options.maxVerifiedSources === undefined || options.maxVerifiedSources >= recordedSources);
|
|
48
|
+
const unverified = collectUnverifiedReasons(status.reasons, Boolean(supplied), sourcesVerified);
|
|
49
|
+
return {
|
|
50
|
+
schema: CONTEXT_GRAPH_FRESHNESS_PREFLIGHT_SCHEMA,
|
|
51
|
+
usable: status.status === 'fresh',
|
|
52
|
+
status: status.status,
|
|
53
|
+
reasons: status.reasons,
|
|
54
|
+
unverified_reasons: unverified,
|
|
55
|
+
coverage: supplied ? 'full' : sourcesVerified ? 'artifacts_and_sources' : 'artifacts_only',
|
|
56
|
+
snapshot_hash: status.snapshotHash,
|
|
57
|
+
generated_at: status.generatedAt,
|
|
58
|
+
node_count: status.nodeCount,
|
|
59
|
+
edge_count: status.edgeCount,
|
|
60
|
+
error_code: status.errorCode,
|
|
61
|
+
repair_command: CONTEXT_GRAPH_REPAIR_COMMAND
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export async function contextGraphFreshnessNote(root, options = {}) {
|
|
65
|
+
const budgetMs = Math.max(1, options.budgetMs ?? 750);
|
|
66
|
+
const preflight = await raceWithTimeout(contextGraphFreshnessPreflight(root, options), budgetMs).catch(() => null);
|
|
67
|
+
if (!preflight)
|
|
68
|
+
return null;
|
|
69
|
+
return contextGraphFreshnessNoteFor(preflight);
|
|
70
|
+
}
|
|
71
|
+
export function contextGraphFreshnessNoteFor(preflight) {
|
|
72
|
+
if (preflight.status === 'fresh')
|
|
73
|
+
return null;
|
|
74
|
+
if (preflight.status === 'missing') {
|
|
75
|
+
return `SKS note: the codebase context graph has not been built (${preflight.error_code}). Run \`${preflight.repair_command}\` before relying on graph-cited context.`;
|
|
76
|
+
}
|
|
77
|
+
if (preflight.status === 'corrupt') {
|
|
78
|
+
return `SKS note: the codebase context graph is unusable (${preflight.error_code}). Run \`${preflight.repair_command}\` to rebuild it; no older generation is substituted.`;
|
|
79
|
+
}
|
|
80
|
+
const reasons = preflight.reasons.join(', ');
|
|
81
|
+
return `SKS note: the codebase context graph is stale (${preflight.error_code}: ${reasons}). Run \`${preflight.repair_command}\` to refresh graph-cited context.`;
|
|
82
|
+
}
|
|
83
|
+
function neutralCacheKey(parts) {
|
|
84
|
+
return { key: contextGraphCacheKey(parts), parts, reusable: true, reasons: [], dirtyPaths: [] };
|
|
85
|
+
}
|
|
86
|
+
function collectUnverifiedReasons(fired, cacheKeySupplied, sourcesVerified) {
|
|
87
|
+
const firedSet = new Set(fired);
|
|
88
|
+
const out = [];
|
|
89
|
+
if (!cacheKeySupplied) {
|
|
90
|
+
for (const reason of CACHE_KEY_DERIVED_REASONS) {
|
|
91
|
+
if (!firedSet.has(reason))
|
|
92
|
+
out.push(reason);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!sourcesVerified && !firedSet.has('source_hash_mismatch'))
|
|
96
|
+
out.push('source_hash_mismatch');
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
async function raceWithTimeout(work, ms) {
|
|
100
|
+
let timer = null;
|
|
101
|
+
const timeout = new Promise((resolve) => {
|
|
102
|
+
timer = setTimeout(() => resolve(null), ms);
|
|
103
|
+
if (timer.unref)
|
|
104
|
+
timer.unref();
|
|
105
|
+
});
|
|
106
|
+
try {
|
|
107
|
+
return await Promise.race([work, timeout]);
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
if (timer)
|
|
111
|
+
clearTimeout(timer);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -350,7 +350,7 @@ async function rebuildHostCapabilityEvidenceForFinalization(input) {
|
|
|
350
350
|
blockers: bound.blockers
|
|
351
351
|
};
|
|
352
352
|
}
|
|
353
|
-
function completeNarutoTerminalBundle(input) {
|
|
353
|
+
export function completeNarutoTerminalBundle(input) {
|
|
354
354
|
const runId = String(input.workflowRunId || '').trim();
|
|
355
355
|
const parent = normalizeSubagentParentSummary(input.parentSummary);
|
|
356
356
|
return Boolean(runId
|
|
@@ -87,6 +87,6 @@ export function interruptedToolOutputRecoveryBlockReason(input = {}) {
|
|
|
87
87
|
`SKS blocked same-thread continuation because custom tool call ${callId} has no correlated output (active mission: ${missionId}).`,
|
|
88
88
|
'The current Codex conversation state may be structurally invalid, so additional context cannot repair the pending Responses request.',
|
|
89
89
|
'Treat the call result as unknown and do not replay a possibly mutating action.',
|
|
90
|
-
'Upgrade the selected codex-lb to
|
|
90
|
+
'Upgrade the selected codex-lb to the official latest stable release (`sks codex-lb status --json` reports whether the deployment satisfies tool-heavy continuation), or explicitly switch with `sks codex-lb use-oauth`, then open a fresh Codex thread and continue the persisted mission after inspecting side effects.'
|
|
91
91
|
].join(' ');
|
|
92
92
|
}
|