@inhuman.tools/turn 0.1.0 → 0.3.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 +38 -0
- package/dist/agent-calls/acceptance.d.ts +1 -1
- package/dist/agent-calls/acceptance.d.ts.map +1 -1
- package/dist/agent-calls/acceptance.js +1 -1
- package/dist/agent-calls/acceptance.js.map +1 -1
- package/dist/agent-calls/callbacks.d.ts +1 -1
- package/dist/agent-calls/callbacks.d.ts.map +1 -1
- package/dist/agent-calls/callbacks.js +1 -1
- package/dist/agent-calls/callbacks.js.map +1 -1
- package/dist/agent-calls/delivery.d.ts +1 -1
- package/dist/agent-calls/delivery.d.ts.map +1 -1
- package/dist/agent-calls/delivery.js +1 -1
- package/dist/agent-calls/delivery.js.map +1 -1
- package/dist/agent-calls/operator.d.ts +1 -1
- package/dist/agent-calls/operator.d.ts.map +1 -1
- package/dist/agent-calls/operator.js +1 -1
- package/dist/agent-calls/operator.js.map +1 -1
- package/dist/agent-calls/runtime.d.ts +4 -2
- package/dist/agent-calls/runtime.d.ts.map +1 -1
- package/dist/agent-calls/runtime.js +17 -11
- package/dist/agent-calls/runtime.js.map +1 -1
- package/dist/agent-calls/types.d.ts +1 -1
- package/dist/agent-calls/types.d.ts.map +1 -1
- package/dist/context/compact-floors.d.ts +1 -1
- package/dist/context/compact-floors.d.ts.map +1 -1
- package/dist/context/compact.d.ts +1 -1
- package/dist/context/compact.d.ts.map +1 -1
- package/dist/context/compact.js +5 -4
- package/dist/context/compact.js.map +1 -1
- package/dist/context/history.d.ts +1 -1
- package/dist/context/history.d.ts.map +1 -1
- package/dist/context/history.js +35 -3
- package/dist/context/history.js.map +1 -1
- package/dist/context/retention/defaults.js +1 -1
- package/dist/context/retention/defaults.js.map +1 -1
- package/dist/context/retention/resolve.js +1 -1
- package/dist/context/retention/resolve.js.map +1 -1
- package/dist/context/retention/schema.js +1 -1
- package/dist/context/retention/schema.js.map +1 -1
- package/dist/context/retention/stamp.d.ts +1 -1
- package/dist/context/retention/stamp.d.ts.map +1 -1
- package/dist/context/retention/stamp.js +5 -2
- package/dist/context/retention/stamp.js.map +1 -1
- package/dist/context/retention/step.d.ts +1 -1
- package/dist/context/retention/step.d.ts.map +1 -1
- package/dist/context/retention/step.js +7 -4
- package/dist/context/retention/step.js.map +1 -1
- package/dist/context/retention/view.d.ts +1 -1
- package/dist/context/retention/view.d.ts.map +1 -1
- package/dist/context/retention/view.js +1 -1
- package/dist/context/retention/view.js.map +1 -1
- package/dist/context/spawn-history.d.ts +13 -0
- package/dist/context/spawn-history.d.ts.map +1 -0
- package/dist/context/spawn-history.js +82 -0
- package/dist/context/spawn-history.js.map +1 -0
- package/dist/context/squeeze.d.ts +1 -1
- package/dist/context/squeeze.d.ts.map +1 -1
- package/dist/context/squeeze.js +1 -1
- package/dist/context/squeeze.js.map +1 -1
- package/dist/context/tools.d.ts +2 -2
- package/dist/context/tools.d.ts.map +1 -1
- package/dist/context/tools.js +2 -2
- package/dist/context/tools.js.map +1 -1
- package/dist/context/transcript.d.ts +3 -1
- package/dist/context/transcript.d.ts.map +1 -1
- package/dist/context/transcript.js +17 -4
- package/dist/context/transcript.js.map +1 -1
- package/dist/policy-processor/classifier.d.ts.map +1 -1
- package/dist/policy-processor/classifier.js +3 -1
- package/dist/policy-processor/classifier.js.map +1 -1
- package/dist/policy-processor/scope.d.ts +1 -0
- package/dist/policy-processor/scope.d.ts.map +1 -1
- package/dist/policy-processor/scope.js +9 -2
- package/dist/policy-processor/scope.js.map +1 -1
- package/dist/policy-processor/unattended-guard.js +8 -2
- package/dist/policy-processor/unattended-guard.js.map +1 -1
- package/dist/resources/compact-assets.d.ts +5 -0
- package/dist/resources/compact-assets.d.ts.map +1 -0
- package/dist/resources/compact-assets.js +6 -0
- package/dist/resources/compact-assets.js.map +1 -0
- package/dist/resources/compact-snapshot.d.ts +21 -0
- package/dist/resources/compact-snapshot.d.ts.map +1 -0
- package/dist/resources/compact-snapshot.js +44 -0
- package/dist/resources/compact-snapshot.js.map +1 -0
- package/dist/resources/index.d.ts +6 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +19 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/module-bundle.d.ts +16 -0
- package/dist/resources/module-bundle.d.ts.map +1 -0
- package/dist/resources/module-bundle.js +78 -0
- package/dist/resources/module-bundle.js.map +1 -0
- package/dist/runtime/ambient-compact.d.ts +38 -0
- package/dist/runtime/ambient-compact.d.ts.map +1 -0
- package/dist/runtime/ambient-compact.js +123 -0
- package/dist/runtime/ambient-compact.js.map +1 -0
- package/dist/runtime/index.d.ts +9 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +8 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/ports.d.ts +33 -0
- package/dist/runtime/ports.d.ts.map +1 -0
- package/dist/runtime/ports.js +2 -0
- package/dist/runtime/ports.js.map +1 -0
- package/dist/runtime/prompt-message-of.d.ts +4 -0
- package/dist/runtime/prompt-message-of.d.ts.map +1 -0
- package/dist/runtime/prompt-message-of.js +8 -0
- package/dist/runtime/prompt-message-of.js.map +1 -0
- package/dist/runtime/re-act-loop-intent-detected-trigger.d.ts +32 -0
- package/dist/runtime/re-act-loop-intent-detected-trigger.d.ts.map +1 -0
- package/dist/runtime/re-act-loop-intent-detected-trigger.js +132 -0
- package/dist/runtime/re-act-loop-intent-detected-trigger.js.map +1 -0
- package/dist/runtime/round-boundary.d.ts +13 -0
- package/dist/runtime/round-boundary.d.ts.map +1 -0
- package/dist/runtime/round-boundary.js +210 -0
- package/dist/runtime/round-boundary.js.map +1 -0
- package/dist/runtime/round-stop.d.ts +9 -0
- package/dist/runtime/round-stop.d.ts.map +1 -0
- package/dist/runtime/round-stop.js +19 -0
- package/dist/runtime/round-stop.js.map +1 -0
- package/dist/runtime/tool-call.d.ts +4 -0
- package/dist/runtime/tool-call.d.ts.map +1 -0
- package/dist/runtime/tool-call.js +8 -0
- package/dist/runtime/tool-call.js.map +1 -0
- package/dist/runtime/turn-iterator.d.ts +32 -0
- package/dist/runtime/turn-iterator.d.ts.map +1 -0
- package/dist/runtime/turn-iterator.js +251 -0
- package/dist/runtime/turn-iterator.js.map +1 -0
- package/dist/runtime/turn-resolvers.d.ts +17 -0
- package/dist/runtime/turn-resolvers.d.ts.map +1 -0
- package/dist/runtime/turn-resolvers.js +29 -0
- package/dist/runtime/turn-resolvers.js.map +1 -0
- package/dist/session/cancel.d.ts +4 -1
- package/dist/session/cancel.d.ts.map +1 -1
- package/dist/session/cancel.js +10 -2
- package/dist/session/cancel.js.map +1 -1
- package/dist/session/commands.d.ts +1 -1
- package/dist/session/commands.d.ts.map +1 -1
- package/dist/session/commands.js +4 -2
- package/dist/session/commands.js.map +1 -1
- package/dist/session/compact-actor.d.ts +12 -5
- package/dist/session/compact-actor.d.ts.map +1 -1
- package/dist/session/compact-actor.js +64 -12
- package/dist/session/compact-actor.js.map +1 -1
- package/dist/session/compact-env.d.ts +31 -5
- package/dist/session/compact-env.d.ts.map +1 -1
- package/dist/session/compact-env.js +23 -13
- package/dist/session/compact-env.js.map +1 -1
- package/dist/session/compact.d.ts +6 -3
- package/dist/session/compact.d.ts.map +1 -1
- package/dist/session/compact.js.map +1 -1
- package/dist/session/index.d.ts +2 -2
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +2 -2
- package/dist/session/index.js.map +1 -1
- package/dist/session/park.d.ts +1 -1
- package/dist/session/park.d.ts.map +1 -1
- package/dist/session/park.js +1 -1
- package/dist/session/park.js.map +1 -1
- package/dist/session/side-channel-reminder.d.ts +1 -1
- package/dist/session/side-channel-reminder.d.ts.map +1 -1
- package/dist/session/side-channel-reminder.js +1 -1
- package/dist/session/side-channel-reminder.js.map +1 -1
- package/dist/session/types.d.ts +1 -1
- package/dist/session/types.d.ts.map +1 -1
- package/dist/session-runtime/ask-line.d.ts +7 -0
- package/dist/session-runtime/ask-line.d.ts.map +1 -0
- package/dist/session-runtime/ask-line.js +36 -0
- package/dist/session-runtime/ask-line.js.map +1 -0
- package/dist/session-runtime/durability.d.ts +25 -0
- package/dist/session-runtime/durability.d.ts.map +1 -0
- package/dist/session-runtime/durability.js +18 -0
- package/dist/session-runtime/durability.js.map +1 -0
- package/dist/session-runtime/effects.d.ts +53 -0
- package/dist/session-runtime/effects.d.ts.map +1 -0
- package/dist/session-runtime/effects.js +18 -0
- package/dist/session-runtime/effects.js.map +1 -0
- package/dist/session-runtime/index.d.ts +8 -0
- package/dist/session-runtime/index.d.ts.map +1 -0
- package/dist/session-runtime/index.js +8 -0
- package/dist/session-runtime/index.js.map +1 -0
- package/dist/session-runtime/interrupt.d.ts +3 -0
- package/dist/session-runtime/interrupt.d.ts.map +1 -0
- package/dist/session-runtime/interrupt.js +33 -0
- package/dist/session-runtime/interrupt.js.map +1 -0
- package/dist/session-runtime/recovery.d.ts +17 -0
- package/dist/session-runtime/recovery.d.ts.map +1 -0
- package/dist/session-runtime/recovery.js +119 -0
- package/dist/session-runtime/recovery.js.map +1 -0
- package/dist/session-runtime/runtime.d.ts +50 -0
- package/dist/session-runtime/runtime.d.ts.map +1 -0
- package/dist/session-runtime/runtime.js +533 -0
- package/dist/session-runtime/runtime.js.map +1 -0
- package/dist/session-runtime/summary.d.ts +26 -0
- package/dist/session-runtime/summary.d.ts.map +1 -0
- package/dist/session-runtime/summary.js +33 -0
- package/dist/session-runtime/summary.js.map +1 -0
- package/dist/tool-catalog/bindings.d.ts +6 -0
- package/dist/tool-catalog/bindings.d.ts.map +1 -0
- package/dist/tool-catalog/bindings.js +37 -0
- package/dist/tool-catalog/bindings.js.map +1 -0
- package/dist/tool-catalog/catalog.d.ts +1 -3
- package/dist/tool-catalog/catalog.d.ts.map +1 -1
- package/dist/tool-catalog/catalog.js +6 -34
- package/dist/tool-catalog/catalog.js.map +1 -1
- package/dist/tool-catalog/types.d.ts +8 -0
- package/dist/tool-catalog/types.d.ts.map +1 -1
- package/dist/tool-catalog/types.js.map +1 -1
- package/dist/tool-runtime/approval/approved.d.ts +1 -1
- package/dist/tool-runtime/approval/approved.d.ts.map +1 -1
- package/dist/tool-runtime/approval/approved.js +1 -1
- package/dist/tool-runtime/approval/approved.js.map +1 -1
- package/dist/tool-runtime/handler.d.ts.map +1 -1
- package/dist/tool-runtime/handler.js +291 -237
- package/dist/tool-runtime/handler.js.map +1 -1
- package/dist/tool-runtime/run-system-tool.d.ts +1 -0
- package/dist/tool-runtime/run-system-tool.d.ts.map +1 -1
- package/dist/tool-runtime/run-system-tool.js +30 -10
- package/dist/tool-runtime/run-system-tool.js.map +1 -1
- package/dist/tool-runtime/worker-tools.d.ts.map +1 -1
- package/dist/tool-runtime/worker-tools.js +2 -2
- package/dist/tool-runtime/worker-tools.js.map +1 -1
- package/package.json +31 -18
- package/src/agent-calls/acceptance.ts +1 -1
- package/src/agent-calls/callbacks.ts +1 -1
- package/src/agent-calls/delivery.ts +1 -1
- package/src/agent-calls/operator.ts +7 -1
- package/src/agent-calls/runtime.ts +23 -11
- package/src/agent-calls/types.ts +1 -1
- package/src/context/compact-floors.ts +1 -1
- package/src/context/compact.ts +4 -5
- package/src/context/history.ts +46 -4
- package/src/context/retention/defaults.ts +1 -1
- package/src/context/retention/resolve.ts +1 -1
- package/src/context/retention/schema.ts +1 -1
- package/src/context/retention/stamp.ts +7 -2
- package/src/context/retention/step.ts +7 -4
- package/src/context/retention/view.ts +1 -1
- package/src/context/spawn-history.ts +95 -0
- package/src/context/squeeze.ts +1 -1
- package/src/context/tools.ts +3 -2
- package/src/context/transcript.ts +15 -4
- package/src/policy-processor/README.md +1 -1
- package/src/policy-processor/classifier.ts +3 -1
- package/src/policy-processor/scope.ts +7 -2
- package/src/policy-processor/unattended-guard.ts +5 -2
- package/src/resources/compact-assets.ts +9 -0
- package/src/resources/compact-snapshot.ts +51 -0
- package/src/resources/index.ts +27 -0
- package/src/resources/module-bundle.ts +90 -0
- package/src/runtime/ambient-compact.ts +147 -0
- package/src/runtime/index.ts +17 -0
- package/src/runtime/ports.ts +28 -0
- package/src/runtime/prompt-message-of.ts +14 -0
- package/src/runtime/re-act-loop-intent-detected-trigger.ts +177 -0
- package/src/runtime/round-boundary.ts +248 -0
- package/src/runtime/round-stop.ts +24 -0
- package/src/runtime/tool-call.ts +10 -0
- package/src/runtime/turn-iterator.ts +312 -0
- package/src/runtime/turn-resolvers.ts +43 -0
- package/src/session/cancel.ts +11 -3
- package/src/session/commands.ts +5 -2
- package/src/session/compact-actor.ts +76 -16
- package/src/session/compact-env.ts +27 -18
- package/src/session/compact.ts +8 -3
- package/src/session/index.ts +7 -2
- package/src/session/park.ts +7 -1
- package/src/session/side-channel-reminder.ts +1 -1
- package/src/session/types.ts +1 -1
- package/src/session-runtime/ask-line.ts +39 -0
- package/src/session-runtime/durability.ts +41 -0
- package/src/session-runtime/effects.ts +35 -0
- package/src/session-runtime/index.ts +19 -0
- package/src/session-runtime/interrupt.ts +34 -0
- package/src/session-runtime/recovery.ts +145 -0
- package/src/session-runtime/runtime.ts +535 -0
- package/src/session-runtime/summary.ts +67 -0
- package/src/tool-catalog/bindings.ts +37 -0
- package/src/tool-catalog/catalog.ts +6 -31
- package/src/tool-catalog/types.ts +9 -0
- package/src/tool-runtime/approval/approved.ts +1 -1
- package/src/tool-runtime/handler.ts +40 -24
- package/src/tool-runtime/run-system-tool.ts +27 -11
- package/src/tool-runtime/worker-tools.ts +2 -2
package/src/session/commands.ts
CHANGED
|
@@ -24,8 +24,9 @@ import {
|
|
|
24
24
|
skillMatchesVerb,
|
|
25
25
|
tapeCompletion,
|
|
26
26
|
userVisualsJson,
|
|
27
|
+
decideRound,
|
|
27
28
|
type UserVisualIdentity,
|
|
28
|
-
} from "@inhuman.tools/harness-model";
|
|
29
|
+
} from "@inhuman.tools/harness-model/session";
|
|
29
30
|
|
|
30
31
|
import type { SessionCommandHost } from "./types.js";
|
|
31
32
|
import { compactRejectReason, stampCompactInputs } from "../context/index.js";
|
|
@@ -73,10 +74,12 @@ function retentionStamp(host: SessionCommandHost): { readonly retentionJson?: st
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
function mintContinueTurn(host: SessionCommandHost, head: SessionTurn): void {
|
|
77
|
+
const continuation = new ReACTChunk();
|
|
78
|
+
continuation.decision = decideRound([{ cause: { kind: "user", sources: [head.uuid] } }]);
|
|
76
79
|
const turn = new SessionTurn({
|
|
77
80
|
turnTail: head,
|
|
78
81
|
prompt: asUserPrompt(""),
|
|
79
|
-
queue: [
|
|
82
|
+
queue: [continuation],
|
|
80
83
|
toolsDefinition: host.toolsFor(head),
|
|
81
84
|
...retentionStamp(host),
|
|
82
85
|
});
|
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
liveJobsCheckIn,
|
|
10
10
|
SessionRoot,
|
|
11
11
|
SessionTurn,
|
|
12
|
-
} from "@inhuman.tools/harness-model";
|
|
12
|
+
} from "@inhuman.tools/harness-model/session";
|
|
13
|
+
import { runInAction } from "mobx";
|
|
13
14
|
|
|
14
15
|
import { applyCompact } from "./compact-env.js";
|
|
15
16
|
import { loadCompactProgram, type CompactProgram } from "./compact.js";
|
|
@@ -22,24 +23,37 @@ import {
|
|
|
22
23
|
textOfCell,
|
|
23
24
|
Transcript,
|
|
24
25
|
} from "../context/index.js";
|
|
26
|
+
import { parseCompactSnapshot, restoreCompactProgram, snapshotCompactProgram } from "../resources/compact-snapshot.js";
|
|
27
|
+
import { pinModuleLoader } from "../resources/module-bundle.js";
|
|
25
28
|
|
|
26
29
|
/** Human tape only. Not injected into the next model prompt. */
|
|
27
30
|
const COMPACT_FAILED_NOTICE = "couldn't compact — try again later";
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
type CompactRunHost = {
|
|
30
33
|
readonly turn: SessionTurn;
|
|
31
34
|
readonly session: SessionRoot;
|
|
32
35
|
readonly compactKeepTurns: number;
|
|
33
|
-
readonly compactStrategy: string;
|
|
34
|
-
readonly kernelFsRoot: string;
|
|
35
36
|
readonly connectionId: string;
|
|
36
37
|
readonly model: string;
|
|
37
38
|
complete?: PromptComplete;
|
|
38
|
-
loadProgram?: (spec: string | undefined, kernelFsRoot: string) => CompactProgram;
|
|
39
39
|
log?: (message: string) => void;
|
|
40
40
|
fail?: (label: string, error: unknown) => void;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
+
export type CompactHost = CompactRunHost &
|
|
44
|
+
(
|
|
45
|
+
| {
|
|
46
|
+
readonly program: CompactProgram | Promise<CompactProgram>;
|
|
47
|
+
checkpoint(job: CompactJob): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
| {
|
|
50
|
+
readonly compactStrategy: string;
|
|
51
|
+
readonly kernelFsRoot: string;
|
|
52
|
+
loadProgram?: (spec: string | undefined, kernelFsRoot: string) => CompactProgram | Promise<CompactProgram>;
|
|
53
|
+
checkpoint?: (job: CompactJob) => Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
|
|
43
57
|
function compactRead(
|
|
44
58
|
configFloor: number,
|
|
45
59
|
kv: Readonly<Record<string, string>>,
|
|
@@ -51,21 +65,56 @@ function compactRead(
|
|
|
51
65
|
}
|
|
52
66
|
|
|
53
67
|
function failCompact(job: CompactJob): void {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
runInAction(() => {
|
|
69
|
+
job.failed = true;
|
|
70
|
+
job.notice = COMPACT_FAILED_NOTICE;
|
|
71
|
+
job.standIn = "";
|
|
72
|
+
job.markComplete();
|
|
73
|
+
});
|
|
58
74
|
}
|
|
59
75
|
|
|
60
|
-
function loadStrategy(host: CompactHost): CompactProgram {
|
|
76
|
+
async function loadStrategy(host: CompactHost): Promise<CompactProgram> {
|
|
77
|
+
if ("program" in host) return host.program;
|
|
61
78
|
return (host.loadProgram ?? loadCompactProgram)(host.compactStrategy, host.kernelFsRoot);
|
|
62
79
|
}
|
|
63
80
|
|
|
81
|
+
async function prepareStrategy(job: CompactJob, host: CompactHost, signal?: AbortSignal): Promise<CompactProgram> {
|
|
82
|
+
signal?.throwIfAborted();
|
|
83
|
+
if (job.resourceSnapshotJson != null && job.resourceSnapshotJson !== "") {
|
|
84
|
+
const program = restoreCompactProgram(parseCompactSnapshot(job.resourceSnapshotJson));
|
|
85
|
+
await host.checkpoint?.(job);
|
|
86
|
+
signal?.throwIfAborted();
|
|
87
|
+
return program;
|
|
88
|
+
}
|
|
89
|
+
const program = await loadStrategy(host);
|
|
90
|
+
const snapshot = snapshotCompactProgram(program);
|
|
91
|
+
const persist = async (): Promise<void> => {
|
|
92
|
+
signal?.throwIfAborted();
|
|
93
|
+
runInAction(() => {
|
|
94
|
+
job.resourceSnapshotJson = JSON.stringify(snapshot);
|
|
95
|
+
});
|
|
96
|
+
await host.checkpoint?.(job);
|
|
97
|
+
signal?.throwIfAborted();
|
|
98
|
+
};
|
|
99
|
+
await persist();
|
|
100
|
+
return program.moduleLoader === undefined
|
|
101
|
+
? program
|
|
102
|
+
: {
|
|
103
|
+
...program,
|
|
104
|
+
moduleLoader: pinModuleLoader(program.moduleLoader, async (path, content) => {
|
|
105
|
+
snapshot.modules[path] = content;
|
|
106
|
+
await persist();
|
|
107
|
+
}),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
64
111
|
/** Abort leaves the job open: an incomplete compact is not a cutoff, and its owner settles it. */
|
|
65
112
|
export async function runCompactJob(job: CompactJob, host: CompactHost, signal?: AbortSignal): Promise<void> {
|
|
113
|
+
if (signal?.aborted) return;
|
|
66
114
|
const rejected = compactRejectReason(host.turn);
|
|
67
115
|
if (rejected != null) {
|
|
68
116
|
host.log?.(`compact ${rejected}`);
|
|
117
|
+
if (signal?.aborted) return;
|
|
69
118
|
job.markComplete();
|
|
70
119
|
return;
|
|
71
120
|
}
|
|
@@ -81,11 +130,13 @@ export async function runCompactJob(job: CompactJob, host: CompactHost, signal?:
|
|
|
81
130
|
const zones = sliceCompactZones(eat, floors);
|
|
82
131
|
if (zones.prefix.cells.length === 0) {
|
|
83
132
|
host.log?.("compact keep-all");
|
|
133
|
+
signal?.throwIfAborted();
|
|
84
134
|
job.markComplete();
|
|
85
135
|
return;
|
|
86
136
|
}
|
|
87
137
|
const focus = job.reason === "user" ? host.turn.prompt.trim() || need?.focus || "" : (need?.focus ?? "");
|
|
88
|
-
const strategy =
|
|
138
|
+
const strategy = await prepareStrategy(job, host, signal);
|
|
139
|
+
signal?.throwIfAborted();
|
|
89
140
|
if (strategy.failsafe != null) {
|
|
90
141
|
// Keep-first is a failsafe, not a strategy: it keeps one cell and drops the
|
|
91
142
|
// rest. Say so, or a broken install looks like a working compaction.
|
|
@@ -98,18 +149,23 @@ export async function runCompactJob(job: CompactJob, host: CompactHost, signal?:
|
|
|
98
149
|
kv: session.kvs,
|
|
99
150
|
forced: job.reason === "user",
|
|
100
151
|
sourcePath: strategy.sourcePath,
|
|
152
|
+
moduleLoader: strategy.moduleLoader,
|
|
101
153
|
job: host.turn,
|
|
102
154
|
complete: host.complete,
|
|
103
155
|
connectionId: host.connectionId === "" ? undefined : host.connectionId,
|
|
104
156
|
completionModel: host.model === "" ? undefined : host.model,
|
|
105
157
|
signal,
|
|
106
158
|
});
|
|
159
|
+
signal?.throwIfAborted();
|
|
107
160
|
if (!result.ok) {
|
|
108
161
|
host.fail?.("compact strategy", result.reason);
|
|
162
|
+
signal?.throwIfAborted();
|
|
109
163
|
failCompact(job);
|
|
110
164
|
return;
|
|
111
165
|
}
|
|
112
|
-
const
|
|
166
|
+
const background = await liveBackgroundJobs(session.motionWork());
|
|
167
|
+
signal?.throwIfAborted();
|
|
168
|
+
const jobs = liveJobsCheckIn(background);
|
|
113
169
|
const body = result.checkIn
|
|
114
170
|
.map((cell) => textOfCell(cell).trim())
|
|
115
171
|
.filter((line) => line !== "")
|
|
@@ -117,16 +173,20 @@ export async function runCompactJob(job: CompactJob, host: CompactHost, signal?:
|
|
|
117
173
|
const checkIn = [jobs, body].filter((line) => line !== "").join("\n\n");
|
|
118
174
|
if (checkIn === "") {
|
|
119
175
|
host.fail?.("compact strategy", "empty-check-in");
|
|
176
|
+
signal?.throwIfAborted();
|
|
120
177
|
failCompact(job);
|
|
121
178
|
return;
|
|
122
179
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
180
|
+
runInAction(() => {
|
|
181
|
+
job.standIn = checkIn;
|
|
182
|
+
job.failed = false;
|
|
183
|
+
job.notice = "";
|
|
184
|
+
job.markComplete();
|
|
185
|
+
});
|
|
127
186
|
} catch (error) {
|
|
128
187
|
if (signal?.aborted) return;
|
|
129
188
|
host.fail?.("compact strategy", error);
|
|
189
|
+
if (signal?.aborted) return;
|
|
130
190
|
failCompact(job);
|
|
131
191
|
}
|
|
132
192
|
}
|
|
@@ -24,8 +24,6 @@
|
|
|
24
24
|
* are `pipe` — pure transforms that mint nothing. `transcript/warm?` is the
|
|
25
25
|
* one argument-reader that stays `source`: `isWarm` reads the live clock.
|
|
26
26
|
*/
|
|
27
|
-
import { dirname } from "node:path";
|
|
28
|
-
|
|
29
27
|
import {
|
|
30
28
|
EnvCapability,
|
|
31
29
|
execState,
|
|
@@ -36,7 +34,7 @@ import {
|
|
|
36
34
|
type SchemeValue,
|
|
37
35
|
} from "@here.build/arrival";
|
|
38
36
|
import { applyCallback, is_callable_value } from "@here.build/arrival/host-internals";
|
|
39
|
-
import { arrivalLoaderCapability } from "@here.build/arrival-modules";
|
|
37
|
+
import { arrivalLoaderCapability, type Loader } from "@here.build/arrival-modules";
|
|
40
38
|
import { promptIsolatedCapability, type PromptComplete } from "@inhuman.tools/env-capability-module-prompt";
|
|
41
39
|
import {
|
|
42
40
|
CompactJob,
|
|
@@ -48,9 +46,8 @@ import {
|
|
|
48
46
|
TurnApprovalNoteChunk,
|
|
49
47
|
TurnNotificationChunk,
|
|
50
48
|
TurnSteerChunk,
|
|
51
|
-
} from "@inhuman.tools/harness-model";
|
|
49
|
+
} from "@inhuman.tools/harness-model/session";
|
|
52
50
|
import { Job } from "@inhuman.tools/plexus-expectations";
|
|
53
|
-
import { programSourceLoader } from "@inhuman.tools/scheme-program/fs";
|
|
54
51
|
import invariant from "tiny-invariant";
|
|
55
52
|
import { z } from "zod";
|
|
56
53
|
|
|
@@ -63,6 +60,7 @@ import {
|
|
|
63
60
|
Transcript,
|
|
64
61
|
type EatCell,
|
|
65
62
|
} from "../context/index.js";
|
|
63
|
+
import { spawnHistoryText } from "../context/spawn-history.js";
|
|
66
64
|
import { charsOf } from "../context/transcript.js";
|
|
67
65
|
|
|
68
66
|
/** Same door as scheme_repl `load`: last-write-wins over `require`. */
|
|
@@ -95,11 +93,10 @@ export type CompactStrategyOpts = {
|
|
|
95
93
|
readonly plan?: string | null;
|
|
96
94
|
readonly forced?: boolean;
|
|
97
95
|
/**
|
|
98
|
-
*
|
|
99
|
-
* `.prompt` pretreats ship next to the strategy — jailed under its dirname.
|
|
100
|
-
* Null for the inline fallback: no `require` surface.
|
|
96
|
+
* Source label for diagnostics; imports require an explicitly supplied loader.
|
|
101
97
|
*/
|
|
102
98
|
readonly sourcePath?: string | null;
|
|
99
|
+
readonly moduleLoader?: Loader;
|
|
103
100
|
/**
|
|
104
101
|
* Live completion wiring for the summarizer call a `.prompt` performs. The
|
|
105
102
|
* prompt capability admits an agent/ask on this job; absent → the strategy
|
|
@@ -316,6 +313,9 @@ export const compactReducerCapability = EnvCapability.define("harness/compact",
|
|
|
316
313
|
if (cell instanceof TurnNotificationChunk) return textOfCell(cell);
|
|
317
314
|
if (cell instanceof TurnApprovalNoteChunk) return `user: ${cell.approvalNoteText()}`;
|
|
318
315
|
if (cell instanceof TurnSteerChunk) return `user: ${cell.completionMessage}`;
|
|
316
|
+
const spawnText =
|
|
317
|
+
cell instanceof ToolCallJob || cell instanceof ProgrammaticJob ? spawnHistoryText(cell) : null;
|
|
318
|
+
if (spawnText !== null) return spawnText;
|
|
319
319
|
if (cell instanceof ToolCallJob) {
|
|
320
320
|
const scheme = cell.children.find(
|
|
321
321
|
(child): child is ProgrammaticJob => child instanceof ProgrammaticJob,
|
|
@@ -404,32 +404,41 @@ export async function applyCompact(
|
|
|
404
404
|
fullTranscript: Transcript,
|
|
405
405
|
opts: CompactStrategyOpts = {},
|
|
406
406
|
): Promise<CompactApplyResult> {
|
|
407
|
+
opts.signal?.throwIfAborted();
|
|
407
408
|
invariant(program.trim() !== "", "compact strategy program is empty");
|
|
408
409
|
const names = opts.forced === true ? (["forced-compact", "compact"] as const) : (["compact"] as const);
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
410
|
+
const suppliedLoader = opts.moduleLoader;
|
|
411
|
+
const loader: Loader | undefined =
|
|
412
|
+
suppliedLoader === undefined
|
|
413
|
+
? undefined
|
|
414
|
+
: {
|
|
415
|
+
resolve: (specifier, fromDir) => suppliedLoader.resolve(specifier, fromDir),
|
|
416
|
+
async read(path) {
|
|
417
|
+
opts.signal?.throwIfAborted();
|
|
418
|
+
const content = await suppliedLoader.read(path);
|
|
419
|
+
opts.signal?.throwIfAborted();
|
|
420
|
+
return content;
|
|
421
|
+
},
|
|
422
|
+
resolvers: suppliedLoader.resolvers,
|
|
423
|
+
};
|
|
415
424
|
// `require` only needs the loader. The isolated host is assembled when a
|
|
416
425
|
// parent job and a connection exist; without them a stub strategy still
|
|
417
426
|
// loads, and `dotprompt/run` is not a symbol.
|
|
418
427
|
const connectionId = opts.connectionId ?? "";
|
|
419
|
-
const isolated =
|
|
428
|
+
const isolated = loader != null && opts.job != null && connectionId !== "";
|
|
420
429
|
const state = await execState(program, {
|
|
421
430
|
capabilities:
|
|
422
|
-
|
|
431
|
+
loader == null
|
|
423
432
|
? [compactReducerCapability]
|
|
424
433
|
: isolated
|
|
425
434
|
? [compactReducerCapability, promptIsolatedCapability, schemeLoadAliasCapability]
|
|
426
435
|
: [compactReducerCapability, schemeLoadAliasCapability],
|
|
427
436
|
config: {
|
|
428
437
|
...compactConfig(opts, fullTranscript.cells),
|
|
429
|
-
...(
|
|
438
|
+
...(loader == null
|
|
430
439
|
? {}
|
|
431
440
|
: {
|
|
432
|
-
loader
|
|
441
|
+
loader,
|
|
433
442
|
dirname: "",
|
|
434
443
|
...(isolated
|
|
435
444
|
? {
|
package/src/session/compact.ts
CHANGED
|
@@ -13,17 +13,22 @@
|
|
|
13
13
|
* `failsafe` set tells the caller to log a diagnostic.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import type { Loader } from "@here.build/arrival-modules";
|
|
17
|
+
|
|
18
|
+
import type { ResourceIdentity } from "../resources/module-bundle.js";
|
|
19
|
+
|
|
16
20
|
const INLINE_KEEP_FIRST =
|
|
17
21
|
"(define (compact compact-transcript full-transcript) (let ((xs (transcript/cells compact-transcript))) (if (null? xs) xs (list (car xs)))))";
|
|
18
22
|
|
|
19
23
|
export type CompactProgram = {
|
|
20
24
|
readonly source: string;
|
|
21
25
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* that has no `compact.prompt` next to it.
|
|
26
|
+
* Source label for diagnostics. Local adapters can supply an absolute path;
|
|
27
|
+
* it never grants module access by itself.
|
|
25
28
|
*/
|
|
26
29
|
readonly sourcePath: string | null;
|
|
30
|
+
readonly resource?: ResourceIdentity;
|
|
31
|
+
readonly moduleLoader?: Loader;
|
|
27
32
|
/**
|
|
28
33
|
* Set only on the inline keep-first failsafe: why no real program could be
|
|
29
34
|
* loaded. The caller must surface it; a silent failsafe would hide a broken
|
package/src/session/index.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
export { type SessionCommandHost } from "./types.js";
|
|
2
|
-
export { failLeftoverApprovables, turnLoopStopped } from "./cancel.js";
|
|
2
|
+
export { failLeftoverApprovables, isWorkReachable, turnLoopStopped } from "./cancel.js";
|
|
3
3
|
export { abortParkableWork, dispatchedMcpSettled, liveEvalWork, mcpJobOnWire } from "./park.js";
|
|
4
4
|
export { admitCompact, admitTurn, cancelTurnCommand, drainPromptQueue, retryTurn, rewindTurn } from "./commands.js";
|
|
5
5
|
export { type CompactProgram, type CompactStrategyResolve, loadCompactProgram } from "./compact.js";
|
|
6
|
-
export {
|
|
6
|
+
export {
|
|
7
|
+
applyCompact,
|
|
8
|
+
compactReducerCapability,
|
|
9
|
+
type CompactApplyResult,
|
|
10
|
+
type CompactStrategyOpts,
|
|
11
|
+
} from "./compact-env.js";
|
|
7
12
|
export { type CompactHost, runCompactJob } from "./compact-actor.js";
|
|
8
13
|
export {
|
|
9
14
|
CONTEXT_NEED_REMINDER,
|
package/src/session/park.ts
CHANGED
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
* program / turn handlers do not seal. On-wire MCP and live eval bags hold
|
|
4
4
|
* the wrap until they settle; undispatched MCP is aborted without a receipt.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
evalProgramOf,
|
|
8
|
+
MCPToolJob,
|
|
9
|
+
McpToolExpectation,
|
|
10
|
+
SessionRoot,
|
|
11
|
+
SessionTurn,
|
|
12
|
+
} from "@inhuman.tools/harness-model/session";
|
|
7
13
|
import { Job, walkWork } from "@inhuman.tools/plexus-expectations";
|
|
8
14
|
|
|
9
15
|
import type { ParkWork } from "./types.js";
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
UPDATE_METADATA_TOOL_NAME,
|
|
35
35
|
type SessionUnit,
|
|
36
36
|
type TurnChunk,
|
|
37
|
-
} from "@inhuman.tools/harness-model";
|
|
37
|
+
} from "@inhuman.tools/harness-model/session";
|
|
38
38
|
|
|
39
39
|
/** Quiet sealed session rounds before the armed round carries the nag. */
|
|
40
40
|
export const SIDE_CHANNEL_REMINDER_EVERY = 5;
|
package/src/session/types.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
SessionSystemPrompt,
|
|
11
11
|
SessionTurn,
|
|
12
12
|
SkillSpec,
|
|
13
|
-
} from "@inhuman.tools/harness-model";
|
|
13
|
+
} from "@inhuman.tools/harness-model/session";
|
|
14
14
|
import type { Job, WorkExpectation } from "@inhuman.tools/plexus-expectations";
|
|
15
15
|
|
|
16
16
|
export type SessionCommandHost = {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glance-sized sitting-ask question. Family + optional description.
|
|
3
|
+
* Not pretty-mcp interpolation — command/path stay off KernelRoot.
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
AgentMessageJob,
|
|
7
|
+
type ApprovableJob,
|
|
8
|
+
BotCallJob,
|
|
9
|
+
CallJob,
|
|
10
|
+
clampChars,
|
|
11
|
+
flattenOverviewLine,
|
|
12
|
+
GLANCE_ASK_DESCRIPTION_MAX_CHARS,
|
|
13
|
+
GLANCE_ASK_LINE_MAX_CHARS,
|
|
14
|
+
MCPToolJob,
|
|
15
|
+
ToolCallJob,
|
|
16
|
+
} from "@inhuman.tools/harness-model/session";
|
|
17
|
+
|
|
18
|
+
export function glanceAskLine(head: ApprovableJob | null): string {
|
|
19
|
+
if (head == null) return "";
|
|
20
|
+
if (!(head instanceof ToolCallJob)) return "";
|
|
21
|
+
const args = head.args;
|
|
22
|
+
const description =
|
|
23
|
+
args != null && typeof args.description === "string"
|
|
24
|
+
? flattenOverviewLine(args.description, GLANCE_ASK_DESCRIPTION_MAX_CHARS)
|
|
25
|
+
: "";
|
|
26
|
+
if (description !== "") return clampChars(`approve ${description}?`, GLANCE_ASK_LINE_MAX_CHARS);
|
|
27
|
+
if (head instanceof BotCallJob) return "confirm bot?";
|
|
28
|
+
if (head instanceof CallJob || head instanceof AgentMessageJob) return "confirm agent?";
|
|
29
|
+
if (head instanceof MCPToolJob) {
|
|
30
|
+
const tool = head.serverToolName.trim() || head.name.trim();
|
|
31
|
+
return tool === "" ? "" : clampChars(`confirm ${tool}?`, GLANCE_ASK_LINE_MAX_CHARS);
|
|
32
|
+
}
|
|
33
|
+
const name = head.name.trim();
|
|
34
|
+
if (name === "shell") return "confirm shell command?";
|
|
35
|
+
if (name === "edit_file") return "confirm edit?";
|
|
36
|
+
if (name === "write_file") return "confirm write?";
|
|
37
|
+
if (name === "") return "";
|
|
38
|
+
return clampChars(`confirm ${name}?`, GLANCE_ASK_LINE_MAX_CHARS);
|
|
39
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type SessionCheckpoint = {
|
|
2
|
+
readonly sessionId: string;
|
|
3
|
+
readonly incarnation: string;
|
|
4
|
+
readonly revision: number;
|
|
5
|
+
readonly state: Uint8Array;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type SessionReceipt = {
|
|
9
|
+
readonly sessionId: string;
|
|
10
|
+
readonly incarnation: string;
|
|
11
|
+
readonly revision: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type SessionDurability =
|
|
15
|
+
| { readonly kind: "memory" }
|
|
16
|
+
| {
|
|
17
|
+
readonly kind: "durable";
|
|
18
|
+
commit(checkpoint: SessionCheckpoint): Promise<SessionReceipt>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function receiptCovers(receipt: SessionReceipt, checkpoint: SessionCheckpoint): boolean {
|
|
22
|
+
return (
|
|
23
|
+
receipt.sessionId === checkpoint.sessionId &&
|
|
24
|
+
receipt.incarnation === checkpoint.incarnation &&
|
|
25
|
+
receipt.revision >= checkpoint.revision
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class SessionPersistenceError extends Error {
|
|
30
|
+
constructor(message = "session checkpoint was not durably acknowledged") {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "SessionPersistenceError";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class SessionOwnershipError extends Error {
|
|
37
|
+
constructor(message = "session execution no longer owns this document") {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "SessionOwnershipError";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export type SessionEffectRequest = {
|
|
4
|
+
readonly sessionId: string;
|
|
5
|
+
readonly operationId: string;
|
|
6
|
+
readonly incarnation: string;
|
|
7
|
+
readonly method: string;
|
|
8
|
+
readonly payload: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const sessionEffectOutcomeSchema = z.discriminatedUnion("kind", [
|
|
12
|
+
z.object({ kind: z.literal("success"), payload: z.string() }),
|
|
13
|
+
z.object({ kind: z.literal("refused"), message: z.string() }),
|
|
14
|
+
z.object({ kind: z.literal("unknown"), message: z.string() }),
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
export type SessionEffectOutcome = z.infer<typeof sessionEffectOutcomeSchema>;
|
|
18
|
+
|
|
19
|
+
export type SessionEffectPort = {
|
|
20
|
+
invoke(request: SessionEffectRequest, signal: AbortSignal): Promise<SessionEffectOutcome>;
|
|
21
|
+
reconcile?(request: SessionEffectRequest, signal: AbortSignal): Promise<SessionEffectOutcome>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const requestSchema = z.object({
|
|
25
|
+
sessionId: z.string(),
|
|
26
|
+
operationId: z.string(),
|
|
27
|
+
incarnation: z.string(),
|
|
28
|
+
method: z.string(),
|
|
29
|
+
payload: z.string(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const effectRecordSchema = z.discriminatedUnion("phase", [
|
|
33
|
+
z.object({ phase: z.literal("intent"), request: requestSchema }),
|
|
34
|
+
z.object({ phase: z.literal("result"), request: requestSchema, outcome: sessionEffectOutcomeSchema }),
|
|
35
|
+
]);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { SessionRuntime, type SessionRecoveryPolicy } from "./runtime.js";
|
|
2
|
+
export {
|
|
3
|
+
receiptCovers,
|
|
4
|
+
SessionOwnershipError,
|
|
5
|
+
SessionPersistenceError,
|
|
6
|
+
type SessionCheckpoint,
|
|
7
|
+
type SessionDurability,
|
|
8
|
+
type SessionReceipt,
|
|
9
|
+
} from "./durability.js";
|
|
10
|
+
export {
|
|
11
|
+
sessionEffectOutcomeSchema,
|
|
12
|
+
type SessionEffectOutcome,
|
|
13
|
+
type SessionEffectPort,
|
|
14
|
+
type SessionEffectRequest,
|
|
15
|
+
} from "./effects.js";
|
|
16
|
+
export { recoverSessionJobs, type JobRecoveryOptions, type RecoveredJob } from "./recovery.js";
|
|
17
|
+
export { interruptSession } from "./interrupt.js";
|
|
18
|
+
export { sessionSummary, observeSessionSummary, type SessionSummary } from "./summary.js";
|
|
19
|
+
export { glanceAskLine } from "./ask-line.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CallJob,
|
|
3
|
+
CompactJob,
|
|
4
|
+
CompletionJob,
|
|
5
|
+
ProgrammaticJob,
|
|
6
|
+
ToolCallJob,
|
|
7
|
+
type SessionRoot,
|
|
8
|
+
} from "@inhuman.tools/harness-model/session";
|
|
9
|
+
import { WorkExpectation, walkWork } from "@inhuman.tools/plexus-expectations";
|
|
10
|
+
import { runInAction } from "mobx";
|
|
11
|
+
|
|
12
|
+
export function interruptSession(root: SessionRoot, message: string): void {
|
|
13
|
+
runInAction(() => {
|
|
14
|
+
for (const turn of root.liveTurns) {
|
|
15
|
+
if (turn.reducerExited) continue;
|
|
16
|
+
for (const node of walkWork(turn)) {
|
|
17
|
+
if (node instanceof CallJob) continue;
|
|
18
|
+
if (node instanceof WorkExpectation && !node.isDone) node.fail("cancel", message, "null");
|
|
19
|
+
if ((node instanceof ToolCallJob || node instanceof ProgrammaticJob) && !node.completed) {
|
|
20
|
+
node.outcomeSnap ??= { ok: false, text: message };
|
|
21
|
+
node.markComplete();
|
|
22
|
+
} else if (node instanceof CompactJob && !node.completed) {
|
|
23
|
+
node.failed = true;
|
|
24
|
+
node.notice = message;
|
|
25
|
+
node.markComplete();
|
|
26
|
+
} else if (node instanceof CompletionJob && !node.completed) node.markComplete();
|
|
27
|
+
}
|
|
28
|
+
turn.cancelRequested = true;
|
|
29
|
+
turn.broke = message;
|
|
30
|
+
turn.reducerExited = true;
|
|
31
|
+
turn.markComplete();
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|