@sanqianx/claude-ai-workbench 0.13.2 → 0.13.3
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/LICENSE +201 -201
- package/README.md +156 -156
- package/apps/agent-terminal/app.css +636 -613
- package/apps/agent-terminal/app.js +125 -65
- package/apps/agent-terminal/conversation-composer.js +44 -0
- package/apps/agent-terminal/index.html +171 -170
- package/apps/agent-terminal/panel-host-proof.html +152 -152
- package/apps/agent-terminal/panel-proof-content.html +41 -41
- package/apps/agent-terminal/right-workspace-dock.css +187 -159
- package/apps/agent-terminal/right-workspace-dock.js +623 -519
- package/apps/ai-profile-settings-host/index.html +25 -25
- package/apps/ai-profile-settings-host/page.css +1 -1
- package/apps/ai-profile-settings-host/page.js +113 -113
- package/apps/minimal-test-host/README.md +15 -15
- package/apps/minimal-test-host/host.css +2 -2
- package/apps/minimal-test-host/host.js +35 -35
- package/apps/minimal-test-host/index.html +58 -58
- package/apps/minimal-test-host/optional-mock/fault-injection.js +15 -15
- package/docs/sdk/FILE_LAYOUT.md +31 -31
- package/docs/sdk/INTEGRATION.md +126 -126
- package/docs/sdk/README.md +21 -21
- package/docs/sdk/SESSION_EVENTS.md +13 -13
- package/docs/sdk/openapi.yaml +8 -1
- package/package.json +1 -1
- package/packages/client/README.md +29 -29
- package/packages/client/index.d.ts +122 -122
- package/packages/client/index.js +260 -260
- package/packages/client/package.json +2 -2
- package/packages/contracts/README.md +9 -9
- package/packages/contracts/index.d.ts +519 -517
- package/packages/contracts/index.js +312 -312
- package/packages/contracts/package.json +1 -1
- package/packages/core/README.md +8 -8
- package/packages/core/index.d.ts +76 -76
- package/packages/core/index.js +222 -222
- package/packages/core/package.json +2 -2
- package/packages/server/README.md +17 -17
- package/packages/server/bin/agent-terminal-server.js +42 -42
- package/packages/server/bin/claude-workbench.js +225 -225
- package/packages/server/index.d.ts +171 -171
- package/packages/server/index.js +230 -230
- package/packages/server/lib/agent-discovery.js +133 -133
- package/packages/server/lib/agent-registry.js +143 -143
- package/packages/server/lib/agent-spawn-env.js +17 -17
- package/packages/server/lib/attachment-store.js +78 -78
- package/packages/server/lib/bridges/app-server-bridge.js +373 -373
- package/packages/server/lib/bridges/opencode-server-bridge.js +506 -506
- package/packages/server/lib/capture-settings-store.js +35 -35
- package/packages/server/lib/claude-history-recovery.js +87 -87
- package/packages/server/lib/cli-session-recovery.js +28 -28
- package/packages/server/lib/context-resolver.js +55 -55
- package/packages/server/lib/conversation-capture.js +51 -51
- package/packages/server/lib/delegation-phase-store.js +357 -357
- package/packages/server/lib/delegation-store.js +474 -468
- package/packages/server/lib/diagnostics.js +32 -32
- package/packages/server/lib/drivers/cli-json-runner.js +974 -974
- package/packages/server/lib/drivers/codex-driver.js +1442 -1442
- package/packages/server/lib/drivers/opencode-driver.js +1554 -1554
- package/packages/server/lib/drivers/zcode-driver.js +1250 -1250
- package/packages/server/lib/drivers/zcode-profile-runtime.js +71 -71
- package/packages/server/lib/event-stream.js +138 -138
- package/packages/server/lib/folder-picker.js +623 -623
- package/packages/server/lib/http-handler.js +339 -339
- package/packages/server/lib/json-file.js +26 -26
- package/packages/server/lib/knowledge-context.js +33 -33
- package/packages/server/lib/model-detection.js +295 -295
- package/packages/server/lib/parent-context.js +44 -0
- package/packages/server/lib/profile-store.js +283 -283
- package/packages/server/lib/project-store.js +102 -102
- package/packages/server/lib/runtime-loader.js +120 -120
- package/packages/server/lib/session-archive-store.js +53 -53
- package/packages/server/lib/session-history.js +612 -612
- package/packages/server/lib/system-doctor.js +125 -125
- package/packages/server/lib/ui-preferences-store.js +48 -48
- package/packages/server/lib/workbench-service.js +1696 -1675
- package/packages/server/package.json +2 -2
- package/packages/ui/README.md +30 -30
- package/packages/ui/ai-profile-settings.d.ts +45 -45
- package/packages/ui/ai-profile-settings.js +283 -283
- package/packages/ui/claude-terminal.d.ts +31 -31
- package/packages/ui/claude-terminal.js +343 -343
- package/packages/ui/index.d.ts +18 -18
- package/packages/ui/index.js +51 -51
- package/packages/ui/package.json +2 -2
- package/packages/ui/theme.css +13 -13
- package/src/assets/claude-prompts.json +21 -21
- package/src/backend/http/ai-profile-routes.js +61 -61
- package/src/backend/http/claude-routes.js +170 -170
- package/src/backend/http/node-helpers.js +45 -45
- package/src/backend/index.js +21 -21
- package/src/backend/lib/ai-adapter.js +144 -144
- package/src/backend/lib/ai-profile-store.js +259 -259
- package/src/backend/lib/ai-vendor-presets.js +83 -83
- package/src/backend/lib/ai-workspace.js +140 -140
- package/src/backend/lib/automation-config.js +253 -253
- package/src/backend/lib/claude-cli-runner.js +2392 -2392
- package/src/backend/lib/data-dir.js +169 -169
- package/src/backend/lib/llm-client.js +176 -176
- package/src/backend/lib/model-context-windows.js +82 -82
- package/src/backend/lib/native-task-control.js +217 -217
- package/src/backend/lib/native-task-history-index.js +475 -475
- package/src/backend/lib/native-task-history-reader.js +526 -526
- package/src/backend/lib/native-task-messages.js +457 -457
- package/src/backend/lib/native-task-registry.js +325 -325
- package/src/backend/lib/native-task-runtime.js +242 -242
- package/src/backend/lib/native-task-trace.js +440 -440
- package/src/backend/lib/native-task-transcript.js +393 -393
- package/src/backend/lib/prompt-registry.js +166 -166
- package/src/backend/lib/runtime-config.js +121 -121
- package/src/backend/lib/sdk-session-stream.js +143 -143
- package/src/backend/lib/sdk-turn-stream.js +16 -16
- package/src/backend/lib/store-scope.js +131 -131
- package/src/backend/lib/task-identity.js +104 -104
- package/src/backend/lib/task-state-projection.js +58 -58
- package/src/index.js +7 -7
- package/src/sdk-wave-a.ts +1 -1
- package/src/sdk-wave-b.ts +1 -1
- package/src/sdk-wave-c.ts +1 -1
- package/src/sdk-wave-d.ts +1 -1
- package/src/sdk-wave-e.ts +1 -1
|
@@ -1,104 +1,104 @@
|
|
|
1
|
-
// Task identity - pure helpers for background-task scope and native-task refs.
|
|
2
|
-
//
|
|
3
|
-
// Canonical shapes (see packages/contracts/index.d.ts):
|
|
4
|
-
// scope: { storeScope, contextId, parentSessionId }
|
|
5
|
-
// native: { kind: 'native-task', ...scope, ownerRunId, taskId }
|
|
6
|
-
//
|
|
7
|
-
// Every field is an already-resolved opaque string kept verbatim: identities
|
|
8
|
-
// are never trimmed or rewritten (leading/trailing spaces are part of the id;
|
|
9
|
-
// only all-whitespace values are invalid). Inputs are snapshotted - returned
|
|
10
|
-
// objects share nothing with the caller input. No owner is derived here:
|
|
11
|
-
// ownerRunId must come from the real parent tool-call relation, and volatile
|
|
12
|
-
// state (runtimeEpoch, ports, PIDs, selected panel, turn numbers, timestamps)
|
|
13
|
-
// never enters a persistent key.
|
|
14
|
-
|
|
15
|
-
const SCOPE_FIELDS = ['storeScope', 'contextId', 'parentSessionId'];
|
|
16
|
-
const NATIVE_TASK_KIND = 'native-task';
|
|
17
|
-
const NATIVE_TASK_KEY_VERSION = 1;
|
|
18
|
-
|
|
19
|
-
function isResolvedId(value) {
|
|
20
|
-
return typeof value === 'string' && value.trim() !== '';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Snapshot the canonical session scope { storeScope, contextId, parentSessionId }.
|
|
25
|
-
* Returns null when input is not an object, or any required field is missing,
|
|
26
|
-
* not a string, or whitespace-only. Extra fields are ignored.
|
|
27
|
-
*/
|
|
28
|
-
function createTaskScope(input) {
|
|
29
|
-
if (!input || typeof input !== 'object') return null;
|
|
30
|
-
const scope = {};
|
|
31
|
-
for (const field of SCOPE_FIELDS) {
|
|
32
|
-
const value = input[field];
|
|
33
|
-
if (!isResolvedId(value)) return null;
|
|
34
|
-
scope[field] = value;
|
|
35
|
-
}
|
|
36
|
-
return scope;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Snapshot a native background task reference. Requires the explicit kind
|
|
41
|
-
* 'native-task' (other kinds must not impersonate native tasks), a complete
|
|
42
|
-
* scope, plus ownerRunId and taskId. ownerRunId is never defaulted from the
|
|
43
|
-
* current turn or any other ambient state.
|
|
44
|
-
*/
|
|
45
|
-
function createNativeTaskRef(input) {
|
|
46
|
-
if (!input || typeof input !== 'object') return null;
|
|
47
|
-
if (input.kind !== NATIVE_TASK_KIND) return null;
|
|
48
|
-
const scope = createTaskScope(input);
|
|
49
|
-
if (!scope) return null;
|
|
50
|
-
if (!isResolvedId(input.ownerRunId)) return null;
|
|
51
|
-
if (!isResolvedId(input.taskId)) return null;
|
|
52
|
-
return {
|
|
53
|
-
kind: NATIVE_TASK_KIND,
|
|
54
|
-
storeScope: scope.storeScope,
|
|
55
|
-
contextId: scope.contextId,
|
|
56
|
-
parentSessionId: scope.parentSessionId,
|
|
57
|
-
ownerRunId: input.ownerRunId,
|
|
58
|
-
taskId: input.taskId,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Persistent key for a native task: an unambiguous JSON tuple with a fixed
|
|
64
|
-
* version and fixed field order, so ids containing separators, quotes or
|
|
65
|
-
* Unicode cannot collide or be rewritten. Returns null for anything that is
|
|
66
|
-
* not a valid native-task reference. Only identity fields participate -
|
|
67
|
-
* runtimeEpoch/ports/PIDs/selected panel/turn/time stay out of the key.
|
|
68
|
-
*/
|
|
69
|
-
function nativeTaskKey(ref) {
|
|
70
|
-
const taskRef = createNativeTaskRef(ref);
|
|
71
|
-
if (!taskRef) return null;
|
|
72
|
-
return JSON.stringify([
|
|
73
|
-
'native-task',
|
|
74
|
-
NATIVE_TASK_KEY_VERSION,
|
|
75
|
-
taskRef.storeScope,
|
|
76
|
-
taskRef.contextId,
|
|
77
|
-
taskRef.parentSessionId,
|
|
78
|
-
taskRef.ownerRunId,
|
|
79
|
-
taskRef.taskId,
|
|
80
|
-
]);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Compare two objects by exactly the three canonical scope fields. Incomplete
|
|
85
|
-
* or invalid scopes never compare equal; other fields (kind, taskId, ...) are
|
|
86
|
-
* ignored, so two different tasks in the same scope still share a scope.
|
|
87
|
-
* This is identity comparison only and carries no authorization meaning -
|
|
88
|
-
* real access checks belong to the API layer.
|
|
89
|
-
*/
|
|
90
|
-
function sameTaskScope(a, b) {
|
|
91
|
-
if (!a || typeof a !== 'object') return false;
|
|
92
|
-
if (!b || typeof b !== 'object') return false;
|
|
93
|
-
for (const field of SCOPE_FIELDS) {
|
|
94
|
-
if (!isResolvedId(a[field]) || a[field] !== b[field]) return false;
|
|
95
|
-
}
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
module.exports = {
|
|
100
|
-
createTaskScope,
|
|
101
|
-
createNativeTaskRef,
|
|
102
|
-
nativeTaskKey,
|
|
103
|
-
sameTaskScope,
|
|
104
|
-
};
|
|
1
|
+
// Task identity - pure helpers for background-task scope and native-task refs.
|
|
2
|
+
//
|
|
3
|
+
// Canonical shapes (see packages/contracts/index.d.ts):
|
|
4
|
+
// scope: { storeScope, contextId, parentSessionId }
|
|
5
|
+
// native: { kind: 'native-task', ...scope, ownerRunId, taskId }
|
|
6
|
+
//
|
|
7
|
+
// Every field is an already-resolved opaque string kept verbatim: identities
|
|
8
|
+
// are never trimmed or rewritten (leading/trailing spaces are part of the id;
|
|
9
|
+
// only all-whitespace values are invalid). Inputs are snapshotted - returned
|
|
10
|
+
// objects share nothing with the caller input. No owner is derived here:
|
|
11
|
+
// ownerRunId must come from the real parent tool-call relation, and volatile
|
|
12
|
+
// state (runtimeEpoch, ports, PIDs, selected panel, turn numbers, timestamps)
|
|
13
|
+
// never enters a persistent key.
|
|
14
|
+
|
|
15
|
+
const SCOPE_FIELDS = ['storeScope', 'contextId', 'parentSessionId'];
|
|
16
|
+
const NATIVE_TASK_KIND = 'native-task';
|
|
17
|
+
const NATIVE_TASK_KEY_VERSION = 1;
|
|
18
|
+
|
|
19
|
+
function isResolvedId(value) {
|
|
20
|
+
return typeof value === 'string' && value.trim() !== '';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Snapshot the canonical session scope { storeScope, contextId, parentSessionId }.
|
|
25
|
+
* Returns null when input is not an object, or any required field is missing,
|
|
26
|
+
* not a string, or whitespace-only. Extra fields are ignored.
|
|
27
|
+
*/
|
|
28
|
+
function createTaskScope(input) {
|
|
29
|
+
if (!input || typeof input !== 'object') return null;
|
|
30
|
+
const scope = {};
|
|
31
|
+
for (const field of SCOPE_FIELDS) {
|
|
32
|
+
const value = input[field];
|
|
33
|
+
if (!isResolvedId(value)) return null;
|
|
34
|
+
scope[field] = value;
|
|
35
|
+
}
|
|
36
|
+
return scope;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Snapshot a native background task reference. Requires the explicit kind
|
|
41
|
+
* 'native-task' (other kinds must not impersonate native tasks), a complete
|
|
42
|
+
* scope, plus ownerRunId and taskId. ownerRunId is never defaulted from the
|
|
43
|
+
* current turn or any other ambient state.
|
|
44
|
+
*/
|
|
45
|
+
function createNativeTaskRef(input) {
|
|
46
|
+
if (!input || typeof input !== 'object') return null;
|
|
47
|
+
if (input.kind !== NATIVE_TASK_KIND) return null;
|
|
48
|
+
const scope = createTaskScope(input);
|
|
49
|
+
if (!scope) return null;
|
|
50
|
+
if (!isResolvedId(input.ownerRunId)) return null;
|
|
51
|
+
if (!isResolvedId(input.taskId)) return null;
|
|
52
|
+
return {
|
|
53
|
+
kind: NATIVE_TASK_KIND,
|
|
54
|
+
storeScope: scope.storeScope,
|
|
55
|
+
contextId: scope.contextId,
|
|
56
|
+
parentSessionId: scope.parentSessionId,
|
|
57
|
+
ownerRunId: input.ownerRunId,
|
|
58
|
+
taskId: input.taskId,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Persistent key for a native task: an unambiguous JSON tuple with a fixed
|
|
64
|
+
* version and fixed field order, so ids containing separators, quotes or
|
|
65
|
+
* Unicode cannot collide or be rewritten. Returns null for anything that is
|
|
66
|
+
* not a valid native-task reference. Only identity fields participate -
|
|
67
|
+
* runtimeEpoch/ports/PIDs/selected panel/turn/time stay out of the key.
|
|
68
|
+
*/
|
|
69
|
+
function nativeTaskKey(ref) {
|
|
70
|
+
const taskRef = createNativeTaskRef(ref);
|
|
71
|
+
if (!taskRef) return null;
|
|
72
|
+
return JSON.stringify([
|
|
73
|
+
'native-task',
|
|
74
|
+
NATIVE_TASK_KEY_VERSION,
|
|
75
|
+
taskRef.storeScope,
|
|
76
|
+
taskRef.contextId,
|
|
77
|
+
taskRef.parentSessionId,
|
|
78
|
+
taskRef.ownerRunId,
|
|
79
|
+
taskRef.taskId,
|
|
80
|
+
]);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Compare two objects by exactly the three canonical scope fields. Incomplete
|
|
85
|
+
* or invalid scopes never compare equal; other fields (kind, taskId, ...) are
|
|
86
|
+
* ignored, so two different tasks in the same scope still share a scope.
|
|
87
|
+
* This is identity comparison only and carries no authorization meaning -
|
|
88
|
+
* real access checks belong to the API layer.
|
|
89
|
+
*/
|
|
90
|
+
function sameTaskScope(a, b) {
|
|
91
|
+
if (!a || typeof a !== 'object') return false;
|
|
92
|
+
if (!b || typeof b !== 'object') return false;
|
|
93
|
+
for (const field of SCOPE_FIELDS) {
|
|
94
|
+
if (!isResolvedId(a[field]) || a[field] !== b[field]) return false;
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = {
|
|
100
|
+
createTaskScope,
|
|
101
|
+
createNativeTaskRef,
|
|
102
|
+
nativeTaskKey,
|
|
103
|
+
sameTaskScope,
|
|
104
|
+
};
|
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Pure projection of one claude/task-* fact onto the session's task index.
|
|
4
|
-
// The raw normalized event is immutable input: it flows untouched into the
|
|
5
|
-
// sequence/SSE/history stream, and only the indexed row is governed here:
|
|
6
|
-
// - known-status whitelist (pending/running/completed/failed/stopped)
|
|
7
|
-
// - the first terminal status is never revived or rewritten by later facts
|
|
8
|
-
// - an unknown or missing status never overwrites an existing row's status
|
|
9
|
-
// - a first-seen task without a usable status is explicitly 'unknown',
|
|
10
|
-
// never defaulted to running or completed
|
|
11
|
-
// Rows are keyed by (sdkSessionId, taskId): a fact without real SDK
|
|
12
|
-
// identity stays in the null bucket instead of borrowing the current
|
|
13
|
-
// SDK session's identity.
|
|
14
|
-
const KNOWN_TASK_STATUSES = new Set(['pending', 'running', 'completed', 'failed', 'stopped']);
|
|
15
|
-
const TERMINAL_TASK_STATUSES = new Set(['completed', 'failed', 'stopped']);
|
|
16
|
-
|
|
17
|
-
function normalizeSdkSessionId(value) {
|
|
18
|
-
return typeof value === 'string' && value !== '' ? value : null;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Returns { index, task } where `task` is a freshly built row (never the
|
|
22
|
-
// prior row, never the event) and `index` is -1 when the caller must insert.
|
|
23
|
-
// Returns null when the fact carries no task id at all.
|
|
24
|
-
function projectTaskStatus(existingTasks, event) {
|
|
25
|
-
if (!event || typeof event !== 'object') return null;
|
|
26
|
-
const taskId = typeof event.taskId === 'string' && event.taskId !== '' ? event.taskId : null;
|
|
27
|
-
if (!taskId) return null;
|
|
28
|
-
const tasks = Array.isArray(existingTasks) ? existingTasks : [];
|
|
29
|
-
const sdkSessionId = normalizeSdkSessionId(event.sdkSessionId);
|
|
30
|
-
const index = tasks.findIndex(task => task && task.taskId === taskId
|
|
31
|
-
&& normalizeSdkSessionId(task.sdkSessionId) === sdkSessionId);
|
|
32
|
-
const prior = index < 0 ? null : tasks[index];
|
|
33
|
-
const incoming = typeof event.status === 'string' && KNOWN_TASK_STATUSES.has(event.status)
|
|
34
|
-
? event.status : null;
|
|
35
|
-
let status;
|
|
36
|
-
let diagnostic = null;
|
|
37
|
-
if (!prior) {
|
|
38
|
-
if (incoming === null) {
|
|
39
|
-
status = 'unknown';
|
|
40
|
-
diagnostic = 'first-seen-unknown-status';
|
|
41
|
-
} else status = incoming;
|
|
42
|
-
} else if (incoming === null) {
|
|
43
|
-
status = prior.status;
|
|
44
|
-
diagnostic = 'unknown-status-kept';
|
|
45
|
-
} else if (TERMINAL_TASK_STATUSES.has(prior.status) && prior.status !== incoming) {
|
|
46
|
-
status = prior.status;
|
|
47
|
-
diagnostic = TERMINAL_TASK_STATUSES.has(incoming) ? 'first-terminal-kept' : 'terminal-not-revived';
|
|
48
|
-
} else {
|
|
49
|
-
status = incoming;
|
|
50
|
-
}
|
|
51
|
-
const task = { ...(prior || {}), ...event };
|
|
52
|
-
task.status = status;
|
|
53
|
-
if (diagnostic) task.diagnostic = diagnostic;
|
|
54
|
-
else delete task.diagnostic;
|
|
55
|
-
return { index, task };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
module.exports = { projectTaskStatus, KNOWN_TASK_STATUSES, TERMINAL_TASK_STATUSES };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Pure projection of one claude/task-* fact onto the session's task index.
|
|
4
|
+
// The raw normalized event is immutable input: it flows untouched into the
|
|
5
|
+
// sequence/SSE/history stream, and only the indexed row is governed here:
|
|
6
|
+
// - known-status whitelist (pending/running/completed/failed/stopped)
|
|
7
|
+
// - the first terminal status is never revived or rewritten by later facts
|
|
8
|
+
// - an unknown or missing status never overwrites an existing row's status
|
|
9
|
+
// - a first-seen task without a usable status is explicitly 'unknown',
|
|
10
|
+
// never defaulted to running or completed
|
|
11
|
+
// Rows are keyed by (sdkSessionId, taskId): a fact without real SDK
|
|
12
|
+
// identity stays in the null bucket instead of borrowing the current
|
|
13
|
+
// SDK session's identity.
|
|
14
|
+
const KNOWN_TASK_STATUSES = new Set(['pending', 'running', 'completed', 'failed', 'stopped']);
|
|
15
|
+
const TERMINAL_TASK_STATUSES = new Set(['completed', 'failed', 'stopped']);
|
|
16
|
+
|
|
17
|
+
function normalizeSdkSessionId(value) {
|
|
18
|
+
return typeof value === 'string' && value !== '' ? value : null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Returns { index, task } where `task` is a freshly built row (never the
|
|
22
|
+
// prior row, never the event) and `index` is -1 when the caller must insert.
|
|
23
|
+
// Returns null when the fact carries no task id at all.
|
|
24
|
+
function projectTaskStatus(existingTasks, event) {
|
|
25
|
+
if (!event || typeof event !== 'object') return null;
|
|
26
|
+
const taskId = typeof event.taskId === 'string' && event.taskId !== '' ? event.taskId : null;
|
|
27
|
+
if (!taskId) return null;
|
|
28
|
+
const tasks = Array.isArray(existingTasks) ? existingTasks : [];
|
|
29
|
+
const sdkSessionId = normalizeSdkSessionId(event.sdkSessionId);
|
|
30
|
+
const index = tasks.findIndex(task => task && task.taskId === taskId
|
|
31
|
+
&& normalizeSdkSessionId(task.sdkSessionId) === sdkSessionId);
|
|
32
|
+
const prior = index < 0 ? null : tasks[index];
|
|
33
|
+
const incoming = typeof event.status === 'string' && KNOWN_TASK_STATUSES.has(event.status)
|
|
34
|
+
? event.status : null;
|
|
35
|
+
let status;
|
|
36
|
+
let diagnostic = null;
|
|
37
|
+
if (!prior) {
|
|
38
|
+
if (incoming === null) {
|
|
39
|
+
status = 'unknown';
|
|
40
|
+
diagnostic = 'first-seen-unknown-status';
|
|
41
|
+
} else status = incoming;
|
|
42
|
+
} else if (incoming === null) {
|
|
43
|
+
status = prior.status;
|
|
44
|
+
diagnostic = 'unknown-status-kept';
|
|
45
|
+
} else if (TERMINAL_TASK_STATUSES.has(prior.status) && prior.status !== incoming) {
|
|
46
|
+
status = prior.status;
|
|
47
|
+
diagnostic = TERMINAL_TASK_STATUSES.has(incoming) ? 'first-terminal-kept' : 'terminal-not-revived';
|
|
48
|
+
} else {
|
|
49
|
+
status = incoming;
|
|
50
|
+
}
|
|
51
|
+
const task = { ...(prior || {}), ...event };
|
|
52
|
+
task.status = status;
|
|
53
|
+
if (diagnostic) task.diagnostic = diagnostic;
|
|
54
|
+
else delete task.diagnostic;
|
|
55
|
+
return { index, task };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = { projectTaskStatus, KNOWN_TASK_STATUSES, TERMINAL_TASK_STATUSES };
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
contracts: require('../packages/contracts'),
|
|
3
|
-
client: require('../packages/client'),
|
|
4
|
-
core: require('../packages/core'),
|
|
5
|
-
server: require('../packages/server'),
|
|
6
|
-
backend: require('./backend')
|
|
7
|
-
};
|
|
1
|
+
module.exports = {
|
|
2
|
+
contracts: require('../packages/contracts'),
|
|
3
|
+
client: require('../packages/client'),
|
|
4
|
+
core: require('../packages/core'),
|
|
5
|
+
server: require('../packages/server'),
|
|
6
|
+
backend: require('./backend')
|
|
7
|
+
};
|
package/src/sdk-wave-a.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
// new wave a
|
|
1
|
+
// new wave a
|
package/src/sdk-wave-b.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
// new wave b
|
|
1
|
+
// new wave b
|
package/src/sdk-wave-c.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
// new wave c
|
|
1
|
+
// new wave c
|
package/src/sdk-wave-d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
// new wave d
|
|
1
|
+
// new wave d
|
package/src/sdk-wave-e.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
// new wave e
|
|
1
|
+
// new wave e
|