@undefineds.co/linx 0.3.5 → 0.3.8
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 +58 -23
- package/dist/generated/version.js +1 -1
- package/dist/generated/version.js.map +1 -1
- package/dist/index.js +336 -162
- package/dist/index.js.map +1 -1
- package/dist/lib/account-session.js +4 -8
- package/dist/lib/account-session.js.map +1 -1
- package/dist/lib/ai-command.js +228 -178
- package/dist/lib/ai-command.js.map +1 -1
- package/dist/lib/auto-mode/archive.js +38 -7
- package/dist/lib/auto-mode/archive.js.map +1 -1
- package/dist/lib/auto-mode/auth.js.map +1 -1
- package/dist/lib/auto-mode/display.js +71 -45
- package/dist/lib/auto-mode/display.js.map +1 -1
- package/dist/lib/auto-mode/format.js +9 -7
- package/dist/lib/auto-mode/format.js.map +1 -1
- package/dist/lib/auto-mode/hooks/claude.js +12 -2
- package/dist/lib/auto-mode/hooks/claude.js.map +1 -1
- package/dist/lib/auto-mode/hooks/codex.js +17 -7
- package/dist/lib/auto-mode/hooks/codex.js.map +1 -1
- package/dist/lib/auto-mode/hooks/index.js +28 -8
- package/dist/lib/auto-mode/hooks/index.js.map +1 -1
- package/dist/lib/auto-mode/pod-ai.js +20 -37
- package/dist/lib/auto-mode/pod-ai.js.map +1 -1
- package/dist/lib/auto-mode/pod-approval.js +124 -195
- package/dist/lib/auto-mode/pod-approval.js.map +1 -1
- package/dist/lib/auto-mode/pod-persistence.js +169 -90
- package/dist/lib/auto-mode/pod-persistence.js.map +1 -1
- package/dist/lib/auto-mode/runner.js +683 -81
- package/dist/lib/auto-mode/runner.js.map +1 -1
- package/dist/lib/auto-mode/secretary.js +186 -41
- package/dist/lib/auto-mode/secretary.js.map +1 -1
- package/dist/lib/auto-mode-command.js +32 -32
- package/dist/lib/auto-mode-command.js.map +1 -1
- package/dist/lib/chat-api.js +242 -50
- package/dist/lib/chat-api.js.map +1 -1
- package/dist/lib/codex-plugin/bridge.js +164 -17
- package/dist/lib/codex-plugin/bridge.js.map +1 -1
- package/dist/lib/codex-plugin/codex-native-proxy.js +370 -34
- package/dist/lib/codex-plugin/codex-native-proxy.js.map +1 -1
- package/dist/lib/credentials-store.js +33 -42
- package/dist/lib/credentials-store.js.map +1 -1
- package/dist/lib/linx-cloud-errors.js +61 -0
- package/dist/lib/linx-cloud-errors.js.map +1 -0
- package/dist/lib/linx-tui-contract.js +8 -5
- package/dist/lib/linx-tui-contract.js.map +1 -1
- package/dist/lib/login-command.js +9 -2
- package/dist/lib/login-command.js.map +1 -1
- package/dist/lib/models.js +3 -20
- package/dist/lib/models.js.map +1 -1
- package/dist/lib/oidc-auth.js +143 -17
- package/dist/lib/oidc-auth.js.map +1 -1
- package/dist/lib/oidc-session-storage.js +2 -6
- package/dist/lib/oidc-session-storage.js.map +1 -1
- package/dist/lib/pi-adapter/auto-input-controller.js +988 -0
- package/dist/lib/pi-adapter/auto-input-controller.js.map +1 -0
- package/dist/lib/pi-adapter/backend-command.js +2 -0
- package/dist/lib/pi-adapter/backend-command.js.map +1 -0
- package/dist/lib/pi-adapter/backend-credentials.js +80 -0
- package/dist/lib/pi-adapter/backend-credentials.js.map +1 -0
- package/dist/lib/pi-adapter/branding.js +246 -108
- package/dist/lib/pi-adapter/branding.js.map +1 -1
- package/dist/lib/pi-adapter/control-state.js +72 -0
- package/dist/lib/pi-adapter/control-state.js.map +1 -0
- package/dist/lib/pi-adapter/interactive.js +2634 -30
- package/dist/lib/pi-adapter/interactive.js.map +1 -1
- package/dist/lib/pi-adapter/pod-approval.js +382 -210
- package/dist/lib/pi-adapter/pod-approval.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror-mapping.js +71 -17
- package/dist/lib/pi-adapter/pod-mirror-mapping.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror.js +531 -64
- package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
- package/dist/lib/pi-adapter/pod-native.js +81 -85
- package/dist/lib/pi-adapter/pod-native.js.map +1 -1
- package/dist/lib/pi-adapter/pod-status-output.js +54 -0
- package/dist/lib/pi-adapter/pod-status-output.js.map +1 -0
- package/dist/lib/pi-adapter/runtime.js +458 -228
- package/dist/lib/pi-adapter/runtime.js.map +1 -1
- package/dist/lib/pi-adapter/session-control.js +509 -0
- package/dist/lib/pi-adapter/session-control.js.map +1 -0
- package/dist/lib/pi-adapter/session.js +35 -22
- package/dist/lib/pi-adapter/session.js.map +1 -1
- package/dist/lib/pi-adapter/stream.js +89 -32
- package/dist/lib/pi-adapter/stream.js.map +1 -1
- package/dist/lib/pi-adapter/sync-recovery.js +89 -0
- package/dist/lib/pi-adapter/sync-recovery.js.map +1 -0
- package/dist/lib/pi-adapter/web-fetch.js +13 -14
- package/dist/lib/pi-adapter/web-fetch.js.map +1 -1
- package/dist/lib/pod-chat-store.js +254 -78
- package/dist/lib/pod-chat-store.js.map +1 -1
- package/dist/lib/pod-data-session.js +156 -35
- package/dist/lib/pod-data-session.js.map +1 -1
- package/dist/lib/solid-auth-store.js +27 -0
- package/dist/lib/solid-auth-store.js.map +1 -0
- package/dist/lib/solid-auth.js +2 -4
- package/dist/lib/solid-auth.js.map +1 -1
- package/dist/lib/solid-client-credentials-login.js +100 -0
- package/dist/lib/solid-client-credentials-login.js.map +1 -0
- package/dist/lib/solid-local-store.js +31 -0
- package/dist/lib/solid-local-store.js.map +1 -0
- package/dist/lib/symphony/archive.js +328 -18
- package/dist/lib/symphony/archive.js.map +1 -1
- package/dist/lib/symphony/pod-projection.js +2222 -0
- package/dist/lib/symphony/pod-projection.js.map +1 -0
- package/dist/lib/symphony-command.js +602 -178
- package/dist/lib/symphony-command.js.map +1 -1
- package/dist/lib/sync-checkpoint-store.js +74 -0
- package/dist/lib/sync-checkpoint-store.js.map +1 -0
- package/dist/skills/symphony/SKILL.md +665 -0
- package/package.json +15 -9
- package/vendor/agent-runtime/dist/agent-runtime.d.ts +137 -0
- package/vendor/agent-runtime/dist/agent-runtime.js +211 -0
- package/vendor/agent-runtime/dist/auto-mode.d.ts +78 -13
- package/vendor/agent-runtime/dist/auto-mode.js +288 -31
- package/vendor/agent-runtime/dist/control-plane.d.ts +28 -0
- package/vendor/agent-runtime/dist/control-plane.js +79 -0
- package/vendor/agent-runtime/dist/file-sync.d.ts +157 -0
- package/vendor/agent-runtime/dist/file-sync.js +314 -0
- package/vendor/agent-runtime/dist/index.d.ts +7 -0
- package/vendor/agent-runtime/dist/index.js +7 -0
- package/vendor/agent-runtime/dist/reconciler.d.ts +117 -0
- package/vendor/agent-runtime/dist/reconciler.js +361 -0
- package/vendor/agent-runtime/dist/symphony.d.ts +128 -8
- package/vendor/agent-runtime/dist/symphony.js +362 -57
- package/vendor/agent-runtime/dist/sync.d.ts +271 -0
- package/vendor/agent-runtime/dist/sync.js +550 -0
- package/vendor/agent-runtime/dist/thread-reconciler-controller.d.ts +58 -0
- package/vendor/agent-runtime/dist/thread-reconciler-controller.js +137 -0
- package/vendor/agent-runtime/dist/turn-controller.js +2 -2
- package/vendor/agent-runtime/dist/wake-scheduler.d.ts +67 -0
- package/vendor/agent-runtime/dist/wake-scheduler.js +194 -0
- package/vendor/agent-runtime/package.json +8 -1
- package/vendor/pi-web-access/CHANGELOG.md +387 -0
- package/vendor/pi-web-access/LICENSE +21 -0
- package/vendor/pi-web-access/README.md +352 -0
- package/vendor/pi-web-access/activity.ts +101 -0
- package/vendor/pi-web-access/banner.png +0 -0
- package/vendor/pi-web-access/chrome-cookies.ts +322 -0
- package/vendor/pi-web-access/code-search.ts +107 -0
- package/vendor/pi-web-access/curator-page.ts +3359 -0
- package/vendor/pi-web-access/curator-server.ts +605 -0
- package/vendor/pi-web-access/exa.ts +520 -0
- package/vendor/pi-web-access/extract.ts +641 -0
- package/vendor/pi-web-access/gemini-api.ts +112 -0
- package/vendor/pi-web-access/gemini-search.ts +361 -0
- package/vendor/pi-web-access/gemini-url-context.ts +126 -0
- package/vendor/pi-web-access/gemini-web-config.ts +52 -0
- package/vendor/pi-web-access/gemini-web.ts +396 -0
- package/vendor/pi-web-access/github-api.ts +196 -0
- package/vendor/pi-web-access/github-extract.ts +634 -0
- package/vendor/pi-web-access/index.ts +2346 -0
- package/vendor/pi-web-access/package.json +45 -0
- package/vendor/pi-web-access/pdf-extract.ts +192 -0
- package/vendor/pi-web-access/perplexity.ts +195 -0
- package/vendor/pi-web-access/pi-web-fetch-demo.mp4 +0 -0
- package/vendor/pi-web-access/rsc-extract.ts +338 -0
- package/vendor/pi-web-access/skills/librarian/SKILL.md +195 -0
- package/vendor/pi-web-access/storage.ts +72 -0
- package/vendor/pi-web-access/summary-review.ts +276 -0
- package/vendor/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +41 -0
- package/vendor/pi-web-access/test/pdf-extract.test.mjs +95 -0
- package/vendor/pi-web-access/utils.ts +44 -0
- package/vendor/pi-web-access/video-extract.ts +378 -0
- package/vendor/pi-web-access/youtube-extract.ts +310 -0
- package/dist/lib/pi-adapter/auth.js +0 -68
- package/dist/lib/pi-adapter/auth.js.map +0 -1
- package/dist/lib/pi-adapter/pod-tools.js +0 -140
- package/dist/lib/pi-adapter/pod-tools.js.map +0 -1
- package/dist/skills/drizzle-solid/SKILL.md +0 -340
- package/dist/skills/pod-storage/SKILL.md +0 -100
- package/dist/skills/solid-modeling/SKILL.md +0 -274
- package/dist/skills/xpod-componentsjs/SKILL.md +0 -284
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { reconcileThreadEvent, summarizeReconcileDecision, } from './reconciler.js';
|
|
2
|
+
import { createWakeJobScheduler, defaultWakeJobDedupeKey, summarizeWakeJobExecutionRecord, summarizeWakeJobSchedulerSnapshot, } from './wake-scheduler.js';
|
|
3
|
+
export function createThreadReconcilerController(options) {
|
|
4
|
+
return new ConfiguredThreadReconcilerController(options);
|
|
5
|
+
}
|
|
6
|
+
export function decideThreadControlEvent(input) {
|
|
7
|
+
const decision = reconcileThreadEvent(input);
|
|
8
|
+
return {
|
|
9
|
+
decision,
|
|
10
|
+
summary: summarizeReconcileDecision(decision),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export async function runThreadReconcilerCycle(options) {
|
|
14
|
+
const { event, dispatchOptions, onDispatched, ...controllerOptions } = options;
|
|
15
|
+
const controller = createThreadReconcilerController({
|
|
16
|
+
...controllerOptions,
|
|
17
|
+
autoStart: false,
|
|
18
|
+
});
|
|
19
|
+
const dispatch = controller.dispatch(event, dispatchOptions);
|
|
20
|
+
await onDispatched?.(dispatch);
|
|
21
|
+
const drain = await controller.startAndDrain();
|
|
22
|
+
return {
|
|
23
|
+
...dispatch,
|
|
24
|
+
...drain,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
class ConfiguredThreadReconcilerController {
|
|
28
|
+
options;
|
|
29
|
+
scheduler;
|
|
30
|
+
contexts = new Map();
|
|
31
|
+
constructor(options) {
|
|
32
|
+
this.options = options;
|
|
33
|
+
this.scheduler = createWakeJobScheduler({
|
|
34
|
+
concurrency: options.concurrency,
|
|
35
|
+
now: options.now,
|
|
36
|
+
dedupeKey: defaultWakeJobDedupeKey,
|
|
37
|
+
handler: async (job, record) => {
|
|
38
|
+
const context = this.contexts.get(record.key);
|
|
39
|
+
if (!context) {
|
|
40
|
+
throw new Error(`No reconciler decision found for wake job ${record.key}`);
|
|
41
|
+
}
|
|
42
|
+
return options.handleWakeJob({
|
|
43
|
+
job,
|
|
44
|
+
record,
|
|
45
|
+
decision: context.decision,
|
|
46
|
+
decisionSummary: context.summary,
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
onQueued: (record) => this.emitWakeHook('queued', record),
|
|
50
|
+
onStarted: (record) => this.emitWakeHook('started', record),
|
|
51
|
+
onCompleted: (record) => this.emitWakeHook('completed', record),
|
|
52
|
+
onFailed: (record) => this.emitWakeHook('failed', record),
|
|
53
|
+
});
|
|
54
|
+
if (options.autoStart !== false) {
|
|
55
|
+
this.scheduler.start();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
dispatch(event, options = {}) {
|
|
59
|
+
const { decision, summary } = decideThreadControlEvent({
|
|
60
|
+
...options,
|
|
61
|
+
policy: this.options.policy,
|
|
62
|
+
event,
|
|
63
|
+
});
|
|
64
|
+
this.options.onDecision?.(summary);
|
|
65
|
+
for (const job of decision.wakeJobs) {
|
|
66
|
+
const key = defaultWakeJobDedupeKey(job);
|
|
67
|
+
if (!this.contexts.has(key)) {
|
|
68
|
+
this.contexts.set(key, {
|
|
69
|
+
decision,
|
|
70
|
+
summary,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const wakeRecords = this.scheduler.submit(decision.wakeJobs);
|
|
75
|
+
for (const record of wakeRecords) {
|
|
76
|
+
if (!this.contexts.has(record.key)) {
|
|
77
|
+
this.contexts.set(record.key, {
|
|
78
|
+
decision,
|
|
79
|
+
summary,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
decision,
|
|
85
|
+
summary,
|
|
86
|
+
wakeRecords,
|
|
87
|
+
wakeRecordSummaries: wakeRecords.map(summarizeWakeJobExecutionRecord),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async dispatchAndDrain(event, options = {}) {
|
|
91
|
+
const dispatch = this.dispatch(event, options);
|
|
92
|
+
const drain = await this.startAndDrain();
|
|
93
|
+
return {
|
|
94
|
+
...dispatch,
|
|
95
|
+
...drain,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async startAndDrain() {
|
|
99
|
+
this.start();
|
|
100
|
+
await this.drain();
|
|
101
|
+
const scheduler = this.snapshot();
|
|
102
|
+
return {
|
|
103
|
+
scheduler,
|
|
104
|
+
schedulerSummary: summarizeWakeJobSchedulerSnapshot(scheduler),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
start() {
|
|
108
|
+
this.scheduler.start();
|
|
109
|
+
}
|
|
110
|
+
stop() {
|
|
111
|
+
this.scheduler.stop();
|
|
112
|
+
}
|
|
113
|
+
drain() {
|
|
114
|
+
return this.scheduler.drain();
|
|
115
|
+
}
|
|
116
|
+
snapshot() {
|
|
117
|
+
return this.scheduler.snapshot();
|
|
118
|
+
}
|
|
119
|
+
emitWakeHook(status, record) {
|
|
120
|
+
const context = this.contexts.get(record.key);
|
|
121
|
+
if (!context) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (status === 'queued') {
|
|
125
|
+
this.options.onWakeJobQueued?.(record, context.summary);
|
|
126
|
+
}
|
|
127
|
+
else if (status === 'started') {
|
|
128
|
+
this.options.onWakeJobStarted?.(record, context.summary);
|
|
129
|
+
}
|
|
130
|
+
else if (status === 'completed') {
|
|
131
|
+
this.options.onWakeJobCompleted?.(record, context.summary);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
this.options.onWakeJobFailed?.(record, context.summary);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -90,7 +90,7 @@ export const GROUP_AGENT_TURN_RULE = {
|
|
|
90
90
|
export const AUTO_MODE_SECRETARY_APPROVAL_RULE = {
|
|
91
91
|
id: 'auto-mode.secretary.approval',
|
|
92
92
|
trigger: 'approval.required',
|
|
93
|
-
targetAgent: '
|
|
93
|
+
targetAgent: '__secretary__',
|
|
94
94
|
targetRole: 'secretary',
|
|
95
95
|
requiredCapabilities: ['approval.request', 'approval.options'],
|
|
96
96
|
context: {
|
|
@@ -108,7 +108,7 @@ export const AUTO_MODE_SECRETARY_APPROVAL_RULE = {
|
|
|
108
108
|
export const AUTO_MODE_SECRETARY_INPUT_RULE = {
|
|
109
109
|
id: 'auto-mode.secretary.input',
|
|
110
110
|
trigger: 'input.required',
|
|
111
|
-
targetAgent: '
|
|
111
|
+
targetAgent: '__secretary__',
|
|
112
112
|
targetRole: 'secretary',
|
|
113
113
|
requiredCapabilities: ['input.structured'],
|
|
114
114
|
context: {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { WakeJob, WakeJobPriority } from './reconciler.js';
|
|
2
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
3
|
+
export type WakeJobExecutionStatus = 'queued' | 'running' | 'completed' | 'failed' | 'cancelled' | 'skipped';
|
|
4
|
+
export interface WakeJobExecutionRecord {
|
|
5
|
+
key: string;
|
|
6
|
+
job: WakeJob;
|
|
7
|
+
status: WakeJobExecutionStatus;
|
|
8
|
+
queuedAt: string;
|
|
9
|
+
startedAt?: string;
|
|
10
|
+
completedAt?: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
result?: unknown;
|
|
13
|
+
}
|
|
14
|
+
export type WakeJobHandler = (job: WakeJob, record: WakeJobExecutionRecord) => MaybePromise<unknown>;
|
|
15
|
+
export interface WakeJobSchedulerOptions {
|
|
16
|
+
handler: WakeJobHandler;
|
|
17
|
+
concurrency?: number;
|
|
18
|
+
now?: () => Date;
|
|
19
|
+
dedupeKey?: (job: WakeJob) => string;
|
|
20
|
+
onQueued?: (record: WakeJobExecutionRecord) => void;
|
|
21
|
+
onStarted?: (record: WakeJobExecutionRecord) => void;
|
|
22
|
+
onCompleted?: (record: WakeJobExecutionRecord) => void;
|
|
23
|
+
onFailed?: (record: WakeJobExecutionRecord) => void;
|
|
24
|
+
}
|
|
25
|
+
export interface WakeJobSchedulerSnapshot {
|
|
26
|
+
queued: WakeJobExecutionRecord[];
|
|
27
|
+
running: WakeJobExecutionRecord[];
|
|
28
|
+
completed: WakeJobExecutionRecord[];
|
|
29
|
+
failed: WakeJobExecutionRecord[];
|
|
30
|
+
cancelled: WakeJobExecutionRecord[];
|
|
31
|
+
skipped: WakeJobExecutionRecord[];
|
|
32
|
+
all: WakeJobExecutionRecord[];
|
|
33
|
+
}
|
|
34
|
+
export interface WakeJobExecutionRecordSummary {
|
|
35
|
+
key: string;
|
|
36
|
+
status: WakeJobExecutionStatus;
|
|
37
|
+
targetAgent: string;
|
|
38
|
+
targetRole: string;
|
|
39
|
+
trigger: string;
|
|
40
|
+
priority: WakeJobPriority;
|
|
41
|
+
queuedAt: string;
|
|
42
|
+
startedAt?: string;
|
|
43
|
+
completedAt?: string;
|
|
44
|
+
error?: string;
|
|
45
|
+
result?: unknown;
|
|
46
|
+
}
|
|
47
|
+
export interface WakeJobSchedulerSnapshotSummary {
|
|
48
|
+
queued: WakeJobExecutionRecordSummary[];
|
|
49
|
+
running: WakeJobExecutionRecordSummary[];
|
|
50
|
+
completed: WakeJobExecutionRecordSummary[];
|
|
51
|
+
failed: WakeJobExecutionRecordSummary[];
|
|
52
|
+
cancelled: WakeJobExecutionRecordSummary[];
|
|
53
|
+
skipped: WakeJobExecutionRecordSummary[];
|
|
54
|
+
}
|
|
55
|
+
export interface WakeJobScheduler {
|
|
56
|
+
submit(job: WakeJob | WakeJob[]): WakeJobExecutionRecord[];
|
|
57
|
+
start(): void;
|
|
58
|
+
stop(): void;
|
|
59
|
+
drain(): Promise<void>;
|
|
60
|
+
snapshot(): WakeJobSchedulerSnapshot;
|
|
61
|
+
get(key: string): WakeJobExecutionRecord | undefined;
|
|
62
|
+
}
|
|
63
|
+
export declare function createWakeJobScheduler(options: WakeJobSchedulerOptions): WakeJobScheduler;
|
|
64
|
+
export declare function defaultWakeJobDedupeKey(job: WakeJob): string;
|
|
65
|
+
export declare function summarizeWakeJobExecutionRecord(record: WakeJobExecutionRecord): WakeJobExecutionRecordSummary;
|
|
66
|
+
export declare function summarizeWakeJobSchedulerSnapshot(snapshot: WakeJobSchedulerSnapshot): WakeJobSchedulerSnapshotSummary;
|
|
67
|
+
export {};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
const PRIORITY_RANK = {
|
|
2
|
+
high: 3,
|
|
3
|
+
normal: 2,
|
|
4
|
+
low: 1,
|
|
5
|
+
};
|
|
6
|
+
export function createWakeJobScheduler(options) {
|
|
7
|
+
return new InMemoryWakeJobScheduler(options);
|
|
8
|
+
}
|
|
9
|
+
export function defaultWakeJobDedupeKey(job) {
|
|
10
|
+
const source = job.sourceEventId ?? job.id ?? job.sourceEventType;
|
|
11
|
+
return [
|
|
12
|
+
job.thread,
|
|
13
|
+
job.chat ?? '',
|
|
14
|
+
job.targetAgent,
|
|
15
|
+
job.targetRole,
|
|
16
|
+
source,
|
|
17
|
+
job.trigger,
|
|
18
|
+
].join('|');
|
|
19
|
+
}
|
|
20
|
+
export function summarizeWakeJobExecutionRecord(record) {
|
|
21
|
+
return {
|
|
22
|
+
key: record.key,
|
|
23
|
+
status: record.status,
|
|
24
|
+
targetAgent: record.job.targetAgent,
|
|
25
|
+
targetRole: record.job.targetRole,
|
|
26
|
+
trigger: record.job.trigger,
|
|
27
|
+
priority: record.job.priority,
|
|
28
|
+
queuedAt: record.queuedAt,
|
|
29
|
+
startedAt: record.startedAt,
|
|
30
|
+
completedAt: record.completedAt,
|
|
31
|
+
error: record.error,
|
|
32
|
+
result: record.result,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function summarizeWakeJobSchedulerSnapshot(snapshot) {
|
|
36
|
+
return {
|
|
37
|
+
queued: snapshot.queued.map(summarizeWakeJobExecutionRecord),
|
|
38
|
+
running: snapshot.running.map(summarizeWakeJobExecutionRecord),
|
|
39
|
+
completed: snapshot.completed.map(summarizeWakeJobExecutionRecord),
|
|
40
|
+
failed: snapshot.failed.map(summarizeWakeJobExecutionRecord),
|
|
41
|
+
cancelled: snapshot.cancelled.map(summarizeWakeJobExecutionRecord),
|
|
42
|
+
skipped: snapshot.skipped.map(summarizeWakeJobExecutionRecord),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
class InMemoryWakeJobScheduler {
|
|
46
|
+
options;
|
|
47
|
+
handler;
|
|
48
|
+
concurrency;
|
|
49
|
+
now;
|
|
50
|
+
dedupeKey;
|
|
51
|
+
records = new Map();
|
|
52
|
+
queue = [];
|
|
53
|
+
running = new Set();
|
|
54
|
+
drainResolvers = [];
|
|
55
|
+
started = false;
|
|
56
|
+
sequence = 0;
|
|
57
|
+
constructor(options) {
|
|
58
|
+
this.options = options;
|
|
59
|
+
this.handler = options.handler;
|
|
60
|
+
this.concurrency = normalizeConcurrency(options.concurrency);
|
|
61
|
+
this.now = options.now ?? (() => new Date());
|
|
62
|
+
this.dedupeKey = options.dedupeKey ?? defaultWakeJobDedupeKey;
|
|
63
|
+
}
|
|
64
|
+
submit(input) {
|
|
65
|
+
const jobs = Array.isArray(input) ? input : [input];
|
|
66
|
+
const records = jobs.map((job) => this.enqueue(job));
|
|
67
|
+
this.pump();
|
|
68
|
+
return records.map(cloneRecord);
|
|
69
|
+
}
|
|
70
|
+
start() {
|
|
71
|
+
this.started = true;
|
|
72
|
+
this.pump();
|
|
73
|
+
}
|
|
74
|
+
stop() {
|
|
75
|
+
this.started = false;
|
|
76
|
+
}
|
|
77
|
+
drain() {
|
|
78
|
+
if (this.isDrained()) {
|
|
79
|
+
return Promise.resolve();
|
|
80
|
+
}
|
|
81
|
+
return new Promise((resolve) => {
|
|
82
|
+
this.drainResolvers.push(resolve);
|
|
83
|
+
this.pump();
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
snapshot() {
|
|
87
|
+
const all = [...this.records.values()].map(cloneRecord);
|
|
88
|
+
return {
|
|
89
|
+
queued: all.filter((record) => record.status === 'queued'),
|
|
90
|
+
running: all.filter((record) => record.status === 'running'),
|
|
91
|
+
completed: all.filter((record) => record.status === 'completed'),
|
|
92
|
+
failed: all.filter((record) => record.status === 'failed'),
|
|
93
|
+
cancelled: all.filter((record) => record.status === 'cancelled'),
|
|
94
|
+
skipped: all.filter((record) => record.status === 'skipped'),
|
|
95
|
+
all,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
get(key) {
|
|
99
|
+
const record = this.records.get(key);
|
|
100
|
+
return record ? cloneRecord(record) : undefined;
|
|
101
|
+
}
|
|
102
|
+
enqueue(job) {
|
|
103
|
+
const key = this.dedupeKey(job);
|
|
104
|
+
const existing = this.records.get(key);
|
|
105
|
+
if (existing) {
|
|
106
|
+
return existing;
|
|
107
|
+
}
|
|
108
|
+
const record = {
|
|
109
|
+
key,
|
|
110
|
+
job,
|
|
111
|
+
status: 'queued',
|
|
112
|
+
queuedAt: this.now().toISOString(),
|
|
113
|
+
};
|
|
114
|
+
this.records.set(key, record);
|
|
115
|
+
this.sequence += 1;
|
|
116
|
+
this.queue.push({
|
|
117
|
+
record,
|
|
118
|
+
sequence: this.sequence,
|
|
119
|
+
});
|
|
120
|
+
this.options.onQueued?.(cloneRecord(record));
|
|
121
|
+
return record;
|
|
122
|
+
}
|
|
123
|
+
pump() {
|
|
124
|
+
while (this.started && this.running.size < this.concurrency && this.queue.length > 0) {
|
|
125
|
+
const queued = this.takeNextQueued();
|
|
126
|
+
this.startRecord(queued.record);
|
|
127
|
+
}
|
|
128
|
+
this.resolveDrainIfIdle();
|
|
129
|
+
}
|
|
130
|
+
takeNextQueued() {
|
|
131
|
+
let bestIndex = 0;
|
|
132
|
+
for (let index = 1; index < this.queue.length; index += 1) {
|
|
133
|
+
if (compareQueuedJobs(this.queue[index], this.queue[bestIndex]) < 0) {
|
|
134
|
+
bestIndex = index;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const [queued] = this.queue.splice(bestIndex, 1);
|
|
138
|
+
return queued;
|
|
139
|
+
}
|
|
140
|
+
startRecord(record) {
|
|
141
|
+
record.status = 'running';
|
|
142
|
+
record.startedAt = this.now().toISOString();
|
|
143
|
+
this.running.add(record.key);
|
|
144
|
+
this.options.onStarted?.(cloneRecord(record));
|
|
145
|
+
void Promise.resolve()
|
|
146
|
+
.then(() => this.handler(record.job, cloneRecord(record)))
|
|
147
|
+
.then((result) => {
|
|
148
|
+
record.status = 'completed';
|
|
149
|
+
record.completedAt = this.now().toISOString();
|
|
150
|
+
record.result = result;
|
|
151
|
+
this.options.onCompleted?.(cloneRecord(record));
|
|
152
|
+
})
|
|
153
|
+
.catch((cause) => {
|
|
154
|
+
const error = cause instanceof Error ? cause.message : String(cause);
|
|
155
|
+
record.status = 'failed';
|
|
156
|
+
record.completedAt = this.now().toISOString();
|
|
157
|
+
record.error = error;
|
|
158
|
+
this.options.onFailed?.(cloneRecord(record));
|
|
159
|
+
})
|
|
160
|
+
.finally(() => {
|
|
161
|
+
this.running.delete(record.key);
|
|
162
|
+
this.pump();
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
isDrained() {
|
|
166
|
+
return this.queue.length === 0 && this.running.size === 0;
|
|
167
|
+
}
|
|
168
|
+
resolveDrainIfIdle() {
|
|
169
|
+
if (!this.isDrained()) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
while (this.drainResolvers.length > 0) {
|
|
173
|
+
this.drainResolvers.shift()?.();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function compareQueuedJobs(left, right) {
|
|
178
|
+
const priority = PRIORITY_RANK[right.record.job.priority] - PRIORITY_RANK[left.record.job.priority];
|
|
179
|
+
return priority === 0 ? left.sequence - right.sequence : priority;
|
|
180
|
+
}
|
|
181
|
+
function normalizeConcurrency(value) {
|
|
182
|
+
if (value === undefined || !Number.isFinite(value)) {
|
|
183
|
+
return 1;
|
|
184
|
+
}
|
|
185
|
+
return Math.max(1, Math.floor(value));
|
|
186
|
+
}
|
|
187
|
+
function cloneRecord(record) {
|
|
188
|
+
return {
|
|
189
|
+
...record,
|
|
190
|
+
job: {
|
|
191
|
+
...record.job,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
@@ -7,7 +7,14 @@
|
|
|
7
7
|
"./acp": "./dist/acp.js",
|
|
8
8
|
"./auto-mode": "./dist/auto-mode.js",
|
|
9
9
|
"./companion-model": "./dist/companion-model.js",
|
|
10
|
+
"./control-plane": "./dist/control-plane.js",
|
|
11
|
+
"./file-sync": "./dist/file-sync.js",
|
|
12
|
+
"./reconciler": "./dist/reconciler.js",
|
|
10
13
|
"./runtime": "./dist/runtime.js",
|
|
11
|
-
"./
|
|
14
|
+
"./symphony": "./dist/symphony.js",
|
|
15
|
+
"./sync": "./dist/sync.js",
|
|
16
|
+
"./thread-reconciler-controller": "./dist/thread-reconciler-controller.js",
|
|
17
|
+
"./turn-controller": "./dist/turn-controller.js",
|
|
18
|
+
"./wake-scheduler": "./dist/wake-scheduler.js"
|
|
12
19
|
}
|
|
13
20
|
}
|