@jungjaehoon/mama-os 0.48.2 → 0.48.4
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/dist/agent/code-act/host-bridge.d.ts.map +1 -1
- package/dist/agent/code-act/host-bridge.js +29 -2
- package/dist/agent/code-act/host-bridge.js.map +1 -1
- package/dist/agent/gateway-tool-executor.d.ts +8 -0
- package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
- package/dist/agent/gateway-tool-executor.js +271 -17
- package/dist/agent/gateway-tool-executor.js.map +1 -1
- package/dist/agent/gateway-tools.md +2 -1
- package/dist/agent/obsidian-cli-args.d.ts +14 -0
- package/dist/agent/obsidian-cli-args.d.ts.map +1 -0
- package/dist/agent/obsidian-cli-args.js +57 -0
- package/dist/agent/obsidian-cli-args.js.map +1 -0
- package/dist/agent/tool-registry.d.ts.map +1 -1
- package/dist/agent/tool-registry.js +23 -2
- package/dist/agent/tool-registry.js.map +1 -1
- package/dist/agent/types.d.ts +6 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +25 -5
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/runtime/api-routes-init.d.ts.map +1 -1
- package/dist/cli/runtime/api-routes-init.js +12 -5
- package/dist/cli/runtime/api-routes-init.js.map +1 -1
- package/dist/gateways/message-router.d.ts.map +1 -1
- package/dist/gateways/message-router.js +1 -0
- package/dist/gateways/message-router.js.map +1 -1
- package/dist/operator/wiki-continuity.d.ts +2 -0
- package/dist/operator/wiki-continuity.d.ts.map +1 -1
- package/dist/operator/wiki-continuity.js +6 -0
- package/dist/operator/wiki-continuity.js.map +1 -1
- package/dist/operator/workorder-hooks.d.ts +3 -3
- package/dist/operator/workorder-hooks.d.ts.map +1 -1
- package/dist/operator/workorder-hooks.js +12 -23
- package/dist/operator/workorder-hooks.js.map +1 -1
- package/dist/wiki/obsidian-writer.d.ts +11 -1
- package/dist/wiki/obsidian-writer.d.ts.map +1 -1
- package/dist/wiki/obsidian-writer.js +98 -4
- package/dist/wiki/obsidian-writer.js.map +1 -1
- package/dist/wiki/wiki-read.d.ts +51 -0
- package/dist/wiki/wiki-read.d.ts.map +1 -0
- package/dist/wiki/wiki-read.js +212 -0
- package/dist/wiki/wiki-read.js.map +1 -0
- package/dist/wiki/wiki-turn-contract.d.ts.map +1 -1
- package/dist/wiki/wiki-turn-contract.js +7 -5
- package/dist/wiki/wiki-turn-contract.js.map +1 -1
- package/dist/wiki-artifacts/types.d.ts +4 -0
- package/dist/wiki-artifacts/types.d.ts.map +1 -1
- package/dist/wiki-artifacts/wiki-publish-adapter.d.ts.map +1 -1
- package/dist/wiki-artifacts/wiki-publish-adapter.js +14 -1
- package/dist/wiki-artifacts/wiki-publish-adapter.js.map +1 -1
- package/package.json +1 -1
|
@@ -82,6 +82,8 @@ const config_manager_js_1 = require("../cli/config/config-manager.js");
|
|
|
82
82
|
const agent_store_js_1 = require("../db/agent-store.js");
|
|
83
83
|
const index_js_1 = require("../envelope/index.js");
|
|
84
84
|
const wiki_publish_adapter_js_1 = require("../wiki-artifacts/wiki-publish-adapter.js");
|
|
85
|
+
const obsidian_cli_args_js_1 = require("./obsidian-cli-args.js");
|
|
86
|
+
const wiki_read_js_1 = require("../wiki/wiki-read.js");
|
|
85
87
|
const changes_projection_js_1 = require("../operator/changes-projection.js");
|
|
86
88
|
const task_list_views_js_1 = require("../operator/task-list-views.js");
|
|
87
89
|
const board_read_views_js_1 = require("../operator/board-read-views.js");
|
|
@@ -526,9 +528,18 @@ class GatewayToolExecutor {
|
|
|
526
528
|
wikiPublishAdapter = null;
|
|
527
529
|
obsidianVaultPath = null;
|
|
528
530
|
obsidianVaultName = null;
|
|
531
|
+
obsidianVaultVerified = false;
|
|
532
|
+
wikiReadCoverage = new Map();
|
|
533
|
+
wikiSourceCoverage = new Map();
|
|
529
534
|
setObsidianVaultPath(vaultPath, vaultName) {
|
|
530
535
|
this.obsidianVaultPath = vaultPath;
|
|
531
536
|
this.obsidianVaultName = vaultName ?? null;
|
|
537
|
+
this.obsidianVaultVerified = false;
|
|
538
|
+
}
|
|
539
|
+
releaseWikiAttemptCoverage(workorderAttemptId) {
|
|
540
|
+
if (Number.isSafeInteger(workorderAttemptId) && workorderAttemptId > 0) {
|
|
541
|
+
this.clearWikiAttemptCoverage(workorderAttemptId);
|
|
542
|
+
}
|
|
532
543
|
}
|
|
533
544
|
taskLedger = null;
|
|
534
545
|
ownerEventEffectLedger = null;
|
|
@@ -2616,12 +2627,50 @@ class GatewayToolExecutor {
|
|
|
2616
2627
|
if (!pagesInput || !Array.isArray(pagesInput)) {
|
|
2617
2628
|
throw new types_js_1.AgentError('wiki_publish requires pages array', 'TOOL_ERROR', undefined, false);
|
|
2618
2629
|
}
|
|
2630
|
+
const wikiAuthority = this.getExecutionState().wikiTaskRange;
|
|
2631
|
+
if (wikiAuthority) {
|
|
2632
|
+
if (!this.obsidianVaultPath) {
|
|
2633
|
+
throw new types_js_1.AgentError('Wiki vault path not configured', 'TOOL_ERROR', undefined, false);
|
|
2634
|
+
}
|
|
2635
|
+
if (wikiAuthority.ownerDate === null) {
|
|
2636
|
+
throw new types_js_1.AgentError('wiki_publish is unavailable for legacy input without a host-issued ownerDate', 'WORKORDER_SUPERSEDED', undefined, false);
|
|
2637
|
+
}
|
|
2638
|
+
try {
|
|
2639
|
+
(0, wiki_read_js_1.assertWikiWorkorderPublish)({
|
|
2640
|
+
root: this.obsidianVaultPath,
|
|
2641
|
+
ownerDate: wikiAuthority.ownerDate,
|
|
2642
|
+
pages: pagesInput,
|
|
2643
|
+
});
|
|
2644
|
+
const attemptId = this.getExecutionState().workorderAttemptId;
|
|
2645
|
+
if (!Number.isSafeInteger(attemptId) || attemptId < 1) {
|
|
2646
|
+
throw new Error('wiki_publish requires a host-issued workorder attempt');
|
|
2647
|
+
}
|
|
2648
|
+
for (const page of pagesInput) {
|
|
2649
|
+
const path = (0, wiki_read_js_1.assertAllowedWikiWorkorderPath)(page.path, wikiAuthority.ownerDate);
|
|
2650
|
+
const coverage = this.wikiReadCoverage.get(this.wikiReadCoverageKey(attemptId, path));
|
|
2651
|
+
if (!coverage?.complete ||
|
|
2652
|
+
coverage.contentVersion !== page.expectedContentVersion) {
|
|
2653
|
+
throw new Error(`wiki_publish page ${path} requires a complete contiguous wiki_read at the expected content version`);
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
catch (error) {
|
|
2658
|
+
throw new types_js_1.AgentError(error instanceof Error ? error.message : 'Wiki publication authority failed', 'TOOL_ERROR', undefined, false);
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
const publishPages = wikiAuthority
|
|
2662
|
+
? pagesInput
|
|
2663
|
+
: pagesInput.map(({ expectedContentVersion: _ignored, ...page }) => page);
|
|
2619
2664
|
const adapter = this.wikiPublishAdapter ??
|
|
2620
2665
|
(0, wiki_publish_adapter_js_1.createWikiPublishAdapter)({
|
|
2621
2666
|
publisher: this.wikiPublisher,
|
|
2622
2667
|
});
|
|
2623
2668
|
try {
|
|
2624
|
-
const publishResult = adapter.publish({ pages:
|
|
2669
|
+
const publishResult = adapter.publish({ pages: publishPages });
|
|
2670
|
+
if (wikiAuthority) {
|
|
2671
|
+
const attemptId = this.getExecutionState().workorderAttemptId;
|
|
2672
|
+
this.clearWikiAttemptCoverage(attemptId);
|
|
2673
|
+
}
|
|
2625
2674
|
return {
|
|
2626
2675
|
success: true,
|
|
2627
2676
|
message: `Wiki published: ${publishResult.pagesPublished} pages`,
|
|
@@ -2632,6 +2681,66 @@ class GatewayToolExecutor {
|
|
|
2632
2681
|
throw new types_js_1.AgentError(error instanceof Error ? error.message : 'Wiki publish failed', 'TOOL_ERROR', undefined, false);
|
|
2633
2682
|
}
|
|
2634
2683
|
}
|
|
2684
|
+
case 'wiki_read': {
|
|
2685
|
+
const wikiAuthority = this.getExecutionState().wikiTaskRange;
|
|
2686
|
+
if (!wikiAuthority || wikiAuthority.ownerDate === null) {
|
|
2687
|
+
throw new types_js_1.AgentError('wiki_read requires a host-issued wiki workorder ownerDate', 'WORKORDER_SUPERSEDED', undefined, false);
|
|
2688
|
+
}
|
|
2689
|
+
if (!this.obsidianVaultPath) {
|
|
2690
|
+
throw new types_js_1.AgentError('Wiki vault path not configured', 'TOOL_ERROR', undefined, false);
|
|
2691
|
+
}
|
|
2692
|
+
try {
|
|
2693
|
+
const attemptId = this.getExecutionState().workorderAttemptId;
|
|
2694
|
+
if (!Number.isSafeInteger(attemptId) || attemptId < 1) {
|
|
2695
|
+
throw new Error('wiki_read requires a host-issued workorder attempt');
|
|
2696
|
+
}
|
|
2697
|
+
const readInput = input;
|
|
2698
|
+
const requestedOffset = readInput.content_offset ?? 0;
|
|
2699
|
+
if (!Number.isSafeInteger(requestedOffset) || requestedOffset < 0) {
|
|
2700
|
+
throw new Error('wiki_read content_offset must be a non-negative safe integer');
|
|
2701
|
+
}
|
|
2702
|
+
const paths = Array.isArray(readInput.paths) ? readInput.paths : [];
|
|
2703
|
+
for (const rawPath of paths) {
|
|
2704
|
+
const path = (0, wiki_read_js_1.assertAllowedWikiWorkorderPath)(rawPath, wikiAuthority.ownerDate);
|
|
2705
|
+
const key = this.wikiReadCoverageKey(attemptId, path);
|
|
2706
|
+
const prior = this.wikiReadCoverage.get(key);
|
|
2707
|
+
if (requestedOffset === 0) {
|
|
2708
|
+
this.wikiReadCoverage.delete(key);
|
|
2709
|
+
}
|
|
2710
|
+
else if (!prior || prior.complete || prior.nextOffset !== requestedOffset) {
|
|
2711
|
+
throw new Error(`wiki_read page ${path} must continue from the host-issued nextContentOffset`);
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
const result = (0, wiki_read_js_1.readWikiPages)({
|
|
2715
|
+
root: this.obsidianVaultPath,
|
|
2716
|
+
ownerDate: wikiAuthority.ownerDate,
|
|
2717
|
+
paths: readInput.paths,
|
|
2718
|
+
contentOffset: readInput.content_offset,
|
|
2719
|
+
contentLimit: readInput.content_limit,
|
|
2720
|
+
});
|
|
2721
|
+
for (const page of result.pages) {
|
|
2722
|
+
const key = this.wikiReadCoverageKey(attemptId, page.path);
|
|
2723
|
+
const prior = this.wikiReadCoverage.get(key);
|
|
2724
|
+
if (requestedOffset > 0 &&
|
|
2725
|
+
prior?.contentVersion !== page.contentVersion) {
|
|
2726
|
+
this.wikiReadCoverage.delete(key);
|
|
2727
|
+
throw new Error(`wiki_read page ${page.path} changed; restart from offset 0`);
|
|
2728
|
+
}
|
|
2729
|
+
this.wikiReadCoverage.set(key, {
|
|
2730
|
+
contentVersion: page.contentVersion,
|
|
2731
|
+
nextOffset: page.nextContentOffset,
|
|
2732
|
+
complete: page.nextContentOffset === null,
|
|
2733
|
+
});
|
|
2734
|
+
}
|
|
2735
|
+
return {
|
|
2736
|
+
success: true,
|
|
2737
|
+
...result,
|
|
2738
|
+
};
|
|
2739
|
+
}
|
|
2740
|
+
catch (error) {
|
|
2741
|
+
throw new types_js_1.AgentError(error instanceof Error ? error.message : 'Wiki read failed', 'TOOL_ERROR', undefined, false);
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2635
2744
|
// Kagemusha query tools — progressive business data exploration
|
|
2636
2745
|
case 'kagemusha_overview': {
|
|
2637
2746
|
const { getOverview } = await import('../connectors/kagemusha/query-tools.js');
|
|
@@ -2761,6 +2870,7 @@ class GatewayToolExecutor {
|
|
|
2761
2870
|
// superseded signal it was before the progressive views.
|
|
2762
2871
|
const temporalContext = this.getExecutionState().temporalWorkContext;
|
|
2763
2872
|
const wikiTaskRange = this.getExecutionState().wikiTaskRange;
|
|
2873
|
+
let taskListInput = input;
|
|
2764
2874
|
if (wikiTaskRange) {
|
|
2765
2875
|
const taskInput = input !== null && typeof input === 'object' && !Array.isArray(input)
|
|
2766
2876
|
? input
|
|
@@ -2768,11 +2878,38 @@ class GatewayToolExecutor {
|
|
|
2768
2878
|
if (wikiTaskRange.updatedSince === null || wikiTaskRange.updatedBefore === null) {
|
|
2769
2879
|
throw new types_js_1.AgentError('Wiki task_list is unavailable for legacy input without a host-issued range', 'WORKORDER_SUPERSEDED', undefined, false);
|
|
2770
2880
|
}
|
|
2771
|
-
if (
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2881
|
+
if (taskInput.view !== undefined && taskInput.view !== 'items') {
|
|
2882
|
+
throw new types_js_1.AgentError('Wiki task_list requires view items', 'TOOL_ERROR', undefined, false);
|
|
2883
|
+
}
|
|
2884
|
+
const allowedFields = new Set([
|
|
2885
|
+
'view',
|
|
2886
|
+
'cursor',
|
|
2887
|
+
'limit',
|
|
2888
|
+
'updated_since',
|
|
2889
|
+
'updated_before',
|
|
2890
|
+
]);
|
|
2891
|
+
const narrowingFields = Object.keys(taskInput).filter((field) => !allowedFields.has(field));
|
|
2892
|
+
if (narrowingFields.length > 0) {
|
|
2893
|
+
throw new types_js_1.AgentError(`Wiki task_list cannot narrow the host-issued range with: ${narrowingFields.join(', ')}`, 'TOOL_ERROR', undefined, false);
|
|
2894
|
+
}
|
|
2895
|
+
if ((taskInput.updated_since !== undefined &&
|
|
2896
|
+
taskInput.updated_since !== wikiTaskRange.updatedSince) ||
|
|
2897
|
+
(taskInput.updated_before !== undefined &&
|
|
2898
|
+
taskInput.updated_before !== wikiTaskRange.updatedBefore)) {
|
|
2899
|
+
throw new types_js_1.AgentError('Wiki task_list parameters contradict the host-issued updated_since/updated_before range', 'TOOL_ERROR', undefined, false);
|
|
2900
|
+
}
|
|
2901
|
+
const normalizedInput = { ...taskInput };
|
|
2902
|
+
if (normalizedInput.cursor === null) {
|
|
2903
|
+
delete normalizedInput.cursor;
|
|
2775
2904
|
}
|
|
2905
|
+
taskListInput = {
|
|
2906
|
+
...normalizedInput,
|
|
2907
|
+
view: 'items',
|
|
2908
|
+
include_terminal: true,
|
|
2909
|
+
order: 'updated',
|
|
2910
|
+
updated_since: wikiTaskRange.updatedSince,
|
|
2911
|
+
updated_before: wikiTaskRange.updatedBefore,
|
|
2912
|
+
};
|
|
2776
2913
|
}
|
|
2777
2914
|
let boundTask;
|
|
2778
2915
|
if (temporalContext) {
|
|
@@ -2782,10 +2919,17 @@ class GatewayToolExecutor {
|
|
|
2782
2919
|
}
|
|
2783
2920
|
boundTask = found;
|
|
2784
2921
|
}
|
|
2785
|
-
|
|
2922
|
+
const taskListResult = (0, task_list_views_js_1.runTaskListView)(taskListInput, {
|
|
2786
2923
|
ledger: this.taskLedger,
|
|
2787
2924
|
boundTask,
|
|
2788
2925
|
});
|
|
2926
|
+
if (wikiTaskRange && taskListResult.success === true) {
|
|
2927
|
+
const nextCursor = taskListResult.nextCursor;
|
|
2928
|
+
if (nextCursor === null || nextCursor === undefined) {
|
|
2929
|
+
this.markWikiSourceCoverage('task_list');
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
return taskListResult;
|
|
2789
2933
|
}
|
|
2790
2934
|
case 'task_external_correlation': {
|
|
2791
2935
|
if (!this.taskLedger) {
|
|
@@ -3197,7 +3341,30 @@ class GatewayToolExecutor {
|
|
|
3197
3341
|
if (!reason || !scope) {
|
|
3198
3342
|
throw new types_js_1.AgentError('contract_no_update requires both reason and scope', 'TOOL_ERROR', undefined, false);
|
|
3199
3343
|
}
|
|
3200
|
-
|
|
3344
|
+
const wikiAuthority = this.getExecutionState().wikiTaskRange;
|
|
3345
|
+
if (wikiAuthority) {
|
|
3346
|
+
if (wikiAuthority.noUpdateScope === null) {
|
|
3347
|
+
throw new types_js_1.AgentError('contract_no_update is unavailable for legacy wiki input without a host-issued scope', 'WORKORDER_SUPERSEDED', undefined, false);
|
|
3348
|
+
}
|
|
3349
|
+
if (scope !== wikiAuthority.noUpdateScope) {
|
|
3350
|
+
throw new types_js_1.AgentError('Wiki contract_no_update scope must match the exact host-issued scope', 'TOOL_ERROR', undefined, false);
|
|
3351
|
+
}
|
|
3352
|
+
const attemptId = this.getExecutionState().workorderAttemptId;
|
|
3353
|
+
if (!Number.isSafeInteger(attemptId) || attemptId < 1) {
|
|
3354
|
+
throw new types_js_1.AgentError('Wiki contract_no_update requires a host-issued workorder attempt', 'WORKORDER_SUPERSEDED', undefined, false);
|
|
3355
|
+
}
|
|
3356
|
+
try {
|
|
3357
|
+
this.assertWikiNoUpdateCoverage(attemptId, wikiAuthority);
|
|
3358
|
+
}
|
|
3359
|
+
catch (error) {
|
|
3360
|
+
throw new types_js_1.AgentError(error instanceof Error ? error.message : 'Wiki source coverage is incomplete', 'TOOL_ERROR', undefined, false);
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
const note = this.taskLedger.recordNoUpdate(scope, reason);
|
|
3364
|
+
if (wikiAuthority) {
|
|
3365
|
+
this.clearWikiAttemptCoverage(this.getExecutionState().workorderAttemptId);
|
|
3366
|
+
}
|
|
3367
|
+
return { success: true, note };
|
|
3201
3368
|
}
|
|
3202
3369
|
case 'agent_notices': {
|
|
3203
3370
|
const rawLimit = Number(input.limit);
|
|
@@ -3919,20 +4086,33 @@ class GatewayToolExecutor {
|
|
|
3919
4086
|
error: 'Wiki vault path not configured',
|
|
3920
4087
|
};
|
|
3921
4088
|
}
|
|
4089
|
+
if (this.obsidianVaultName && !this.obsidianVaultVerified) {
|
|
4090
|
+
try {
|
|
4091
|
+
const { stdout } = await execFileAsync('obsidian', (0, obsidian_cli_args_js_1.buildObsidianCliArgs)('vault', undefined, this.obsidianVaultName), {
|
|
4092
|
+
timeout: 15000,
|
|
4093
|
+
encoding: 'utf-8',
|
|
4094
|
+
maxBuffer: 1024 * 1024,
|
|
4095
|
+
});
|
|
4096
|
+
const configuredPath = (0, fs_1.realpathSync)(this.obsidianVaultPath);
|
|
4097
|
+
const selectedPath = (0, fs_1.realpathSync)((0, obsidian_cli_args_js_1.parseObsidianVaultPath)(stdout));
|
|
4098
|
+
if (selectedPath !== configuredPath) {
|
|
4099
|
+
throw new Error(`Obsidian CLI selected ${selectedPath}, expected configured wiki vault ${configuredPath}`);
|
|
4100
|
+
}
|
|
4101
|
+
this.obsidianVaultVerified = true;
|
|
4102
|
+
}
|
|
4103
|
+
catch (error) {
|
|
4104
|
+
throw new types_js_1.AgentError(error instanceof Error ? error.message : 'Obsidian vault target verification failed', 'TOOL_ERROR', undefined, false);
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
3922
4107
|
// Obsidian CLI syntax: obsidian <command> key=value ... [flags]
|
|
3923
4108
|
// Without vault=<name> the CLI targets the FOCUSED vault, so wiki writes
|
|
3924
4109
|
// could land in whatever vault the owner has open. Pin it when configured.
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
cliArgs.
|
|
4110
|
+
let cliArgs;
|
|
4111
|
+
try {
|
|
4112
|
+
cliArgs = (0, obsidian_cli_args_js_1.buildObsidianCliArgs)(command, args, this.obsidianVaultName);
|
|
3928
4113
|
}
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
cliArgs.push(key);
|
|
3932
|
-
}
|
|
3933
|
-
else {
|
|
3934
|
-
cliArgs.push(`${key}=${value}`);
|
|
3935
|
-
}
|
|
4114
|
+
catch (error) {
|
|
4115
|
+
throw new types_js_1.AgentError(error instanceof Error ? error.message : 'Invalid Obsidian CLI arguments', 'TOOL_ERROR', undefined, false);
|
|
3936
4116
|
}
|
|
3937
4117
|
try {
|
|
3938
4118
|
const { stdout } = await execFileAsync('obsidian', cliArgs, {
|
|
@@ -3959,6 +4139,47 @@ class GatewayToolExecutor {
|
|
|
3959
4139
|
};
|
|
3960
4140
|
}
|
|
3961
4141
|
}
|
|
4142
|
+
wikiReadCoverageKey(attemptId, path) {
|
|
4143
|
+
return `${attemptId}:${path}`;
|
|
4144
|
+
}
|
|
4145
|
+
markWikiSourceCoverage(source) {
|
|
4146
|
+
const attemptId = this.getExecutionState().workorderAttemptId;
|
|
4147
|
+
if (!Number.isSafeInteger(attemptId) || attemptId < 1) {
|
|
4148
|
+
return;
|
|
4149
|
+
}
|
|
4150
|
+
const current = this.wikiSourceCoverage.get(attemptId) ?? {
|
|
4151
|
+
contextCompileComplete: false,
|
|
4152
|
+
taskListComplete: false,
|
|
4153
|
+
};
|
|
4154
|
+
this.wikiSourceCoverage.set(attemptId, {
|
|
4155
|
+
contextCompileComplete: current.contextCompileComplete || source === 'context_compile',
|
|
4156
|
+
taskListComplete: current.taskListComplete || source === 'task_list',
|
|
4157
|
+
});
|
|
4158
|
+
}
|
|
4159
|
+
assertWikiNoUpdateCoverage(attemptId, authority) {
|
|
4160
|
+
if (authority.ownerDate === null) {
|
|
4161
|
+
throw new Error('Wiki no-update coverage requires a host-issued ownerDate');
|
|
4162
|
+
}
|
|
4163
|
+
const sources = this.wikiSourceCoverage.get(attemptId);
|
|
4164
|
+
if (!sources?.contextCompileComplete || !sources.taskListComplete) {
|
|
4165
|
+
throw new Error('Wiki contract_no_update requires completed context_compile and all bounded task_list pages');
|
|
4166
|
+
}
|
|
4167
|
+
for (const path of ['Home.md', `daily/${authority.ownerDate}.md`]) {
|
|
4168
|
+
const read = this.wikiReadCoverage.get(this.wikiReadCoverageKey(attemptId, path));
|
|
4169
|
+
if (!read?.complete) {
|
|
4170
|
+
throw new Error('Wiki contract_no_update requires complete wiki_read coverage for Home.md and the bound daily page');
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
clearWikiAttemptCoverage(attemptId) {
|
|
4175
|
+
this.wikiSourceCoverage.delete(attemptId);
|
|
4176
|
+
const prefix = `${attemptId}:`;
|
|
4177
|
+
for (const key of this.wikiReadCoverage.keys()) {
|
|
4178
|
+
if (key.startsWith(prefix)) {
|
|
4179
|
+
this.wikiReadCoverage.delete(key);
|
|
4180
|
+
}
|
|
4181
|
+
}
|
|
4182
|
+
}
|
|
3962
4183
|
async executeCodeAct(input) {
|
|
3963
4184
|
const { CodeActSandbox, CodeActToolPolicyValidationError, CODE_ACT_MUTATION_COMMITTED_AFTER_ABORT, CODE_ACT_MUTATION_OUTCOME_UNKNOWN, HostBridge, projectCodeActToolPolicy, } = await import('./code-act/index.js');
|
|
3964
4185
|
const sandbox = new CodeActSandbox();
|
|
@@ -4254,6 +4475,36 @@ class GatewayToolExecutor {
|
|
|
4254
4475
|
let effectiveInput = temporalContext
|
|
4255
4476
|
? { ...input, task: bindTemporalContextPacketTask(temporalContext, input.task) }
|
|
4256
4477
|
: input;
|
|
4478
|
+
const wikiAuthority = ctx.wikiTaskRange;
|
|
4479
|
+
if (wikiAuthority) {
|
|
4480
|
+
if (wikiAuthority.rangeStartMs === null ||
|
|
4481
|
+
wikiAuthority.rangeEndMs === null ||
|
|
4482
|
+
wikiAuthority.connectors === null) {
|
|
4483
|
+
throw new Error('context_compile is unavailable for legacy wiki input');
|
|
4484
|
+
}
|
|
4485
|
+
const providedRange = input.range;
|
|
4486
|
+
if (providedRange !== undefined &&
|
|
4487
|
+
(providedRange.start_ms !== wikiAuthority.rangeStartMs ||
|
|
4488
|
+
providedRange.end_ms !== wikiAuthority.rangeEndMs)) {
|
|
4489
|
+
throw new Error('context_compile range contradicts the host-issued wiki range');
|
|
4490
|
+
}
|
|
4491
|
+
if (input.connectors !== undefined) {
|
|
4492
|
+
const providedConnectors = [...input.connectors].sort();
|
|
4493
|
+
const allowedConnectors = [...wikiAuthority.connectors].sort();
|
|
4494
|
+
if (providedConnectors.length !== allowedConnectors.length ||
|
|
4495
|
+
providedConnectors.some((connector, index) => connector !== allowedConnectors[index])) {
|
|
4496
|
+
throw new Error('context_compile connectors contradict the host-issued wiki connector scope');
|
|
4497
|
+
}
|
|
4498
|
+
}
|
|
4499
|
+
effectiveInput = {
|
|
4500
|
+
...effectiveInput,
|
|
4501
|
+
range: {
|
|
4502
|
+
start_ms: wikiAuthority.rangeStartMs,
|
|
4503
|
+
end_ms: wikiAuthority.rangeEndMs,
|
|
4504
|
+
},
|
|
4505
|
+
connectors: [...wikiAuthority.connectors],
|
|
4506
|
+
};
|
|
4507
|
+
}
|
|
4257
4508
|
// (d)-by-construction (S2 measurement: 94% of reconcile rejections were
|
|
4258
4509
|
// packets carrying only recalled memories). The HOST knows the bound
|
|
4259
4510
|
// source - the task row holds it raw; the context carries only hashes -
|
|
@@ -4331,6 +4582,9 @@ class GatewayToolExecutor {
|
|
|
4331
4582
|
!temporalPacketRawSourcesWithinBoundSource(temporalContext, result.packet.source_refs, reviewWindow)) {
|
|
4332
4583
|
throw new Error('context_compile packet exceeds the active temporal task source');
|
|
4333
4584
|
}
|
|
4585
|
+
if (wikiAuthority) {
|
|
4586
|
+
this.markWikiSourceCoverage('context_compile');
|
|
4587
|
+
}
|
|
4334
4588
|
return {
|
|
4335
4589
|
success: true,
|
|
4336
4590
|
packet: result.packet,
|