maka-agent 0.2.0-dev.25.20260908 → 0.2.0-dev.26.20260909
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/mcp-capability-provider.js +4 -1
- package/dist/pi-tui-mcp-status.js +2 -2
- package/dist/pi-tui-pickers.js +19 -29
- package/dist/tui-autocomplete-layout.js +3 -1
- package/dist/tui-copy-catalog.js +6 -3
- package/dist/tui-editor-render.js +36 -0
- package/dist/tui-mcp-control.js +41 -6
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/runtime-event.js +3 -0
- package/node_modules/@maka/core/dist/session.js +29 -4
- package/node_modules/@maka/core/dist/workhub-action-result.js +68 -0
- package/node_modules/@maka/core/package.json +2 -3
- package/node_modules/@maka/eval/dist/toolchain-verification.js +1 -1
- package/node_modules/@maka/mcp/dist/form-elicitation.js +213 -0
- package/node_modules/@maka/mcp/dist/index.js +337 -71
- package/node_modules/@maka/runtime/dist/agent-run.js +10 -1
- package/node_modules/@maka/runtime/dist/goal-continuation.js +5 -1
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +20 -1
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +120 -13
- package/node_modules/@maka/runtime/dist/session-manager.js +20 -36
- package/node_modules/@maka/runtime-host/dist/client/connection.js +30 -5
- package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +17 -1
- package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +4 -2
- package/node_modules/@maka/runtime-host/dist/client/peer-client.js +18 -1
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +5 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +3 -2
- package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +68 -168
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/canonical-session-projection.js +2 -1
- package/node_modules/@maka/runtime-host/dist/server/connection-session.js +28 -8
- package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +112 -56
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +45 -20
- package/node_modules/@maka/runtime-host/dist/server/failure-diagnostic.js +24 -0
- package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +7 -3
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +3 -3
- package/node_modules/@maka/runtime-host/dist/server/host-session-availability.js +5 -2
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +3 -2
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +34 -4
- package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +4 -1
- package/node_modules/@maka/runtime-host/dist/server/operation-dispatcher.js +2 -7
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +110 -14
- package/node_modules/@maka/runtime-host/dist/server/runtime-resource-coordinator.js +11 -8
- package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +31 -21
- package/node_modules/@maka/runtime-host/dist/server/serial-outbound-writer.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +20 -8
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +15 -104
- package/node_modules/@maka/runtime-host/dist/server/shared-session-transcript.js +3 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +44 -85
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +120 -137
- package/node_modules/@maka/storage/dist/agent-run-store.js +14 -1
- package/node_modules/@maka/storage/dist/artifact-store.js +40 -3
- package/node_modules/@maka/storage/dist/atomic-file-write.js +74 -0
- package/node_modules/@maka/storage/dist/credential-store.js +12 -63
- package/node_modules/@maka/storage/dist/execution-stores.js +4 -1
- package/node_modules/@maka/storage/dist/mcp-config-store.js +10 -25
- package/node_modules/@maka/storage/dist/session-store.js +14 -2
- package/node_modules/@maka/storage/dist/settings-store.js +8 -4
- package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +11 -0
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +12 -1
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +67 -3
- package/node_modules/@maka/storage/dist/stable-storage.js +14 -1
- package/package.json +1 -1
- package/node_modules/@maka/core/dist/workhub-creation-intent.js +0 -990
- package/node_modules/@maka/core/dist/workhub-session-resolver.js +0 -50
|
@@ -16,10 +16,11 @@
|
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
|
-
import {
|
|
20
|
-
import { chmod, mkdir, open, readFile, rename, rm } from 'node:fs/promises';
|
|
19
|
+
import { readFile } from 'node:fs/promises';
|
|
21
20
|
import { dirname, join } from 'node:path';
|
|
21
|
+
import { writeAtomicFile } from './atomic-file-write.js';
|
|
22
22
|
import { withFileUpdateLock } from './file-update-lock.js';
|
|
23
|
+
import { hardenDirectory } from './stable-storage.js';
|
|
23
24
|
/** Current on-disk schema version. Unknown versions fail closed on read. */
|
|
24
25
|
export const CREDENTIAL_SCHEMA_VERSION = 1;
|
|
25
26
|
export function createFileCredentialStore(workspaceRoot) {
|
|
@@ -129,68 +130,14 @@ class FileCredentialStore {
|
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
132
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* write plaintext credentials into a dir we couldn't lock down); no-op on
|
|
137
|
-
* Windows. Shared by the writer and the lock so their dir hardening can't drift.
|
|
138
|
-
*/
|
|
139
|
-
async function ensureSecretDir(dir) {
|
|
140
|
-
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
141
|
-
await chmodStrict(dir, 0o700);
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Owner-only atomic write for a credentials file: a 0700 dir, an exclusive
|
|
145
|
-
* 0600 temp ('wx'/O_EXCL so we never follow a pre-planted symlink at a
|
|
146
|
-
* predictable path), a durability fence before and after the atomic rename,
|
|
147
|
-
* and temp cleanup on failure.
|
|
133
|
+
* Owner-only atomic write for a credentials file. Directory hardening is an
|
|
134
|
+
* explicit credential-store policy; file publication uses the shared legacy
|
|
135
|
+
* JSON writer so its mode, synchronization, and cleanup behavior remains
|
|
136
|
+
* aligned with settings and MCP config.
|
|
148
137
|
*/
|
|
149
138
|
async function writeSecretFileAtomic(path, contents) {
|
|
150
|
-
await
|
|
151
|
-
|
|
152
|
-
try {
|
|
153
|
-
const handle = await open(tempPath, 'wx', 0o600);
|
|
154
|
-
try {
|
|
155
|
-
await handle.writeFile(contents, 'utf8');
|
|
156
|
-
await handle.sync();
|
|
157
|
-
}
|
|
158
|
-
finally {
|
|
159
|
-
await handle.close();
|
|
160
|
-
}
|
|
161
|
-
await chmodStrict(tempPath, 0o600);
|
|
162
|
-
await rename(tempPath, path);
|
|
163
|
-
await syncDirectory(dirname(path));
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
166
|
-
await rm(tempPath, { force: true });
|
|
167
|
-
throw error;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
async function syncDirectory(path) {
|
|
171
|
-
if (process.platform === 'win32')
|
|
172
|
-
return;
|
|
173
|
-
const handle = await open(path, 'r');
|
|
174
|
-
try {
|
|
175
|
-
await handle.sync();
|
|
176
|
-
}
|
|
177
|
-
finally {
|
|
178
|
-
await handle.close();
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* chmod that fails loud on POSIX and is best-effort on Windows. A secret file
|
|
183
|
-
* or its directory left looser than intended breaks the 0600/0700 boundary, so
|
|
184
|
-
* on POSIX we surface the failure rather than write plaintext into it; Windows
|
|
185
|
-
* has no POSIX mode, so a failure there is a no-op. One policy for both the
|
|
186
|
-
* secret file (0600) and its directory (0700) so they can't drift apart.
|
|
187
|
-
*/
|
|
188
|
-
async function chmodStrict(path, mode) {
|
|
189
|
-
if (process.platform === 'win32') {
|
|
190
|
-
await chmod(path, mode).catch(() => { });
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
await chmod(path, mode);
|
|
139
|
+
await hardenDirectory(dirname(path));
|
|
140
|
+
await writeAtomicFile(path, contents, { fileMode: 0o600 });
|
|
194
141
|
}
|
|
195
142
|
const LOCK_TIMEOUT_MS = 10_000;
|
|
196
143
|
/**
|
|
@@ -220,7 +167,9 @@ const LOCK_TIMEOUT_MS = 10_000;
|
|
|
220
167
|
* stays the typed store and callers can't drive the lock directly.
|
|
221
168
|
*/
|
|
222
169
|
export async function withCredentialFileLock(targetPath, fn, timeoutMs = LOCK_TIMEOUT_MS) {
|
|
223
|
-
|
|
170
|
+
// Same owner-only hardening the writer applies, so the lock directory can
|
|
171
|
+
// never sit looser than the secret it guards.
|
|
172
|
+
await hardenDirectory(dirname(targetPath));
|
|
224
173
|
return withFileUpdateLock(targetPath, fn, timeoutMs);
|
|
225
174
|
}
|
|
226
175
|
const STORED_CREDENTIAL_KINDS = [
|
|
@@ -149,10 +149,13 @@ async function createExecutionStoresForWrite(lease, kind, extension) {
|
|
|
149
149
|
listForRecovery: () => run(() => sessionStore.listForRecovery()),
|
|
150
150
|
readHeaderSnapshot: (sessionId) => run(() => sessionStore.readHeaderSnapshot(sessionId)),
|
|
151
151
|
readHeaderRecordSnapshot: (sessionId) => run(() => sessionStore.readHeaderRecordSnapshot(sessionId)),
|
|
152
|
-
readCatalogRecord: (sessionId) => run(() => sessionStore.readCatalogRecord(sessionId)),
|
|
152
|
+
readCatalogRecord: (sessionId, roleScope) => run(() => sessionStore.readCatalogRecord(sessionId, roleScope)),
|
|
153
153
|
probeSessionRemoval: (sessionId) => run(() => sessionStore.probeSessionRemoval(sessionId)),
|
|
154
154
|
readMessagesSnapshot: (sessionId) => run(() => sessionStore.readMessagesSnapshot(sessionId)),
|
|
155
155
|
readTranscriptMessagesSnapshot: (sessionId, request) => run(() => sessionStore.readTranscriptMessagesSnapshot(sessionId, request)),
|
|
156
|
+
readCoordinationTranscriptIndexState: () => run(() => sessionStore.readCoordinationTranscriptIndexState()),
|
|
157
|
+
appendCoordinationTranscriptIndex: (records) => run(() => sessionStore.appendCoordinationTranscriptIndex(records)),
|
|
158
|
+
readCoordinationTranscriptIndex: (request) => run(() => sessionStore.readCoordinationTranscriptIndex(request)),
|
|
156
159
|
readTranscriptHighWaterSnapshot: (sessionId) => run(() => sessionStore.readTranscriptHighWaterSnapshot(sessionId)),
|
|
157
160
|
listTurnsSnapshot: (sessionId) => run(() => sessionStore.listTurnsSnapshot(sessionId)),
|
|
158
161
|
readHeader: (sessionId) => run(() => sessionStore.readHeader(sessionId)),
|
|
@@ -16,11 +16,15 @@
|
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
|
-
import {
|
|
20
|
-
import { chmod, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
19
|
+
import { readFile } from 'node:fs/promises';
|
|
21
20
|
import { dirname, join } from 'node:path';
|
|
22
21
|
import { MCP_CONFIG_VERSION, createDefaultMcpConfig, isNonLoopbackCleartextHttp, } from '@maka/core/mcp';
|
|
22
|
+
import { writeAtomicFile } from './atomic-file-write.js';
|
|
23
23
|
import { withProcessLifetimeFileUpdateLock } from './process-lifetime-file-update-lock.js';
|
|
24
|
+
import { hardenDirectory } from './stable-storage.js';
|
|
25
|
+
// Consumers reconcile an already-published write through this store's public
|
|
26
|
+
// boundary; the shared atomic writer itself remains internal to storage.
|
|
27
|
+
export { AtomicFileWriteCommitUnknownError } from './atomic-file-write.js';
|
|
24
28
|
const MAX_SERVERS = 100;
|
|
25
29
|
const MAX_ID_LENGTH = 128;
|
|
26
30
|
const MAX_STRING_LENGTH = 8_192;
|
|
@@ -157,34 +161,15 @@ class FileMcpConfigStore {
|
|
|
157
161
|
return withProcessLifetimeFileUpdateLock(this.path, operation);
|
|
158
162
|
}
|
|
159
163
|
async write(config) {
|
|
160
|
-
const dir = dirname(this.path);
|
|
161
164
|
await this.ensureDirectory();
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
encoding: 'utf8',
|
|
166
|
-
mode: 0o600,
|
|
167
|
-
flag: 'wx',
|
|
168
|
-
});
|
|
169
|
-
if (process.platform !== 'win32')
|
|
170
|
-
await chmod(tempPath, 0o600);
|
|
171
|
-
await rename(tempPath, this.path);
|
|
172
|
-
if (process.platform !== 'win32')
|
|
173
|
-
await chmod(this.path, 0o600);
|
|
174
|
-
}
|
|
175
|
-
finally {
|
|
176
|
-
await rm(tempPath, { force: true }).catch(() => { });
|
|
177
|
-
}
|
|
165
|
+
await writeAtomicFile(this.path, `${JSON.stringify(config, null, 2)}\n`, {
|
|
166
|
+
fileMode: 0o600,
|
|
167
|
+
});
|
|
178
168
|
}
|
|
179
169
|
ensureDirectory() {
|
|
180
170
|
if (this.directoryReady)
|
|
181
171
|
return this.directoryReady;
|
|
182
|
-
const
|
|
183
|
-
const ready = (async () => {
|
|
184
|
-
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
185
|
-
if (process.platform !== 'win32')
|
|
186
|
-
await chmod(dir, 0o700);
|
|
187
|
-
})();
|
|
172
|
+
const ready = hardenDirectory(dirname(this.path), 0o700);
|
|
188
173
|
this.directoryReady = ready;
|
|
189
174
|
void ready.catch(() => {
|
|
190
175
|
if (this.directoryReady === ready)
|
|
@@ -351,9 +351,9 @@ class SqliteSessionStore {
|
|
|
351
351
|
// pre-cutover session sees its imported rows.
|
|
352
352
|
return projectHeaderSnapshot(await this.metadata.read(sessionId));
|
|
353
353
|
}
|
|
354
|
-
async readCatalogRecord(sessionId) {
|
|
354
|
+
async readCatalogRecord(sessionId, roleScope = 'ordinary') {
|
|
355
355
|
await this.ensureCatalogProjectionReadable();
|
|
356
|
-
const record = await this.metadata.readCatalogRecord(sessionId);
|
|
356
|
+
const record = await this.metadata.readCatalogRecord(sessionId, roleScope);
|
|
357
357
|
return {
|
|
358
358
|
...projectHeaderSnapshot(record),
|
|
359
359
|
activityAt: record.activityAt,
|
|
@@ -372,6 +372,18 @@ class SqliteSessionStore {
|
|
|
372
372
|
await this.ensureReady();
|
|
373
373
|
return this.metadata.readTranscriptHighWater(sessionId);
|
|
374
374
|
}
|
|
375
|
+
async readCoordinationTranscriptIndexState() {
|
|
376
|
+
await this.ensureReady();
|
|
377
|
+
return this.metadata.readCoordinationTranscriptIndexState();
|
|
378
|
+
}
|
|
379
|
+
async appendCoordinationTranscriptIndex(records) {
|
|
380
|
+
await this.ensureReady();
|
|
381
|
+
return this.metadata.appendCoordinationTranscriptIndex(records);
|
|
382
|
+
}
|
|
383
|
+
async readCoordinationTranscriptIndex(request) {
|
|
384
|
+
await this.ensureReady();
|
|
385
|
+
return this.metadata.readCoordinationTranscriptIndex(request);
|
|
386
|
+
}
|
|
375
387
|
async listTurnsSnapshot(sessionId) {
|
|
376
388
|
return deriveTurnRecords(await this.readMessagesSnapshot(sessionId));
|
|
377
389
|
}
|
|
@@ -16,10 +16,11 @@
|
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
|
-
import { mkdir, readFile
|
|
19
|
+
import { mkdir, readFile } from 'node:fs/promises';
|
|
20
20
|
import { dirname, join } from 'node:path';
|
|
21
21
|
import { createDefaultSettings, mergeSettings, normalizeSettings } from '@maka/core/settings';
|
|
22
22
|
import { sanitizeOnboardingMilestones } from '@maka/core/onboarding';
|
|
23
|
+
import { writeAtomicFile } from './atomic-file-write.js';
|
|
23
24
|
export function createSettingsStore(workspaceRoot) {
|
|
24
25
|
return new FileSettingsStore(workspaceRoot);
|
|
25
26
|
}
|
|
@@ -133,10 +134,13 @@ class FileSettingsStore {
|
|
|
133
134
|
return result;
|
|
134
135
|
}
|
|
135
136
|
async write(settings) {
|
|
137
|
+
// SettingsStore does not own the workspace directory's permission policy:
|
|
138
|
+
// sibling stores such as MCP config may independently harden the same root.
|
|
139
|
+
// Keep both directory creation and the historical umask-derived file mode.
|
|
136
140
|
await mkdir(dirname(this.settingsPath), { recursive: true });
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
141
|
+
await writeAtomicFile(this.settingsPath, JSON.stringify(settings, null, 2) + '\n', {
|
|
142
|
+
fileMode: 0o666 & ~process.umask(),
|
|
143
|
+
});
|
|
140
144
|
}
|
|
141
145
|
withQueue(operation) {
|
|
142
146
|
const next = this.queue.then(operation, operation);
|
|
@@ -82,6 +82,17 @@ class SqliteArtifactMetadataRepository {
|
|
|
82
82
|
.prepare('SELECT 1 FROM artifact_records WHERE relative_path = ?')
|
|
83
83
|
.get(relativePath));
|
|
84
84
|
}
|
|
85
|
+
readRelativePathsByCaseFoldedArtifactIds(artifactIds) {
|
|
86
|
+
this.assertOpen();
|
|
87
|
+
if (artifactIds.length === 0)
|
|
88
|
+
return [];
|
|
89
|
+
const placeholders = artifactIds.map(() => '?').join(', ');
|
|
90
|
+
const rows = this.#lease.database
|
|
91
|
+
.prepare(`SELECT relative_path FROM artifact_records
|
|
92
|
+
WHERE artifact_id COLLATE NOCASE IN (${placeholders})`)
|
|
93
|
+
.all(...artifactIds);
|
|
94
|
+
return rows.map((row) => row.relative_path);
|
|
95
|
+
}
|
|
85
96
|
forgetUpgradeOrphanPaths(relativePaths) {
|
|
86
97
|
this.assertOpen();
|
|
87
98
|
this.#lease.transaction('write', () => {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
|
-
export const SQLITE_SESSION_METADATA_SCHEMA_VERSION =
|
|
19
|
+
export const SQLITE_SESSION_METADATA_SCHEMA_VERSION = 39;
|
|
20
20
|
export const SQLITE_SESSION_MESSAGE_CHUNK_BYTES = 64 * 1024;
|
|
21
21
|
export const SQLITE_SESSION_MESSAGE_CHUNK_MARKER = '{"$maka":"session-message-chunks-v1"}';
|
|
22
22
|
export const SQLITE_AGENT_GRAPH_CONTROL_TABLES = [
|
|
@@ -32,6 +32,17 @@ export const SQLITE_AGENT_GRAPH_CONTROL_TABLES = [
|
|
|
32
32
|
'agent_graph_supervisor_wake_attempts',
|
|
33
33
|
];
|
|
34
34
|
const MIGRATIONS = new Map([
|
|
35
|
+
[
|
|
36
|
+
39,
|
|
37
|
+
`
|
|
38
|
+
CREATE TABLE IF NOT EXISTS coordination_transcript_index (
|
|
39
|
+
sequence INTEGER PRIMARY KEY,
|
|
40
|
+
source TEXT NOT NULL CHECK (source IN ('legacy', 'runtime')),
|
|
41
|
+
source_sequence INTEGER NOT NULL CHECK (source_sequence >= 0),
|
|
42
|
+
UNIQUE (source, source_sequence)
|
|
43
|
+
);
|
|
44
|
+
`,
|
|
45
|
+
],
|
|
35
46
|
[
|
|
36
47
|
1,
|
|
37
48
|
`
|
|
@@ -730,10 +730,12 @@ export class SqliteSessionMetadataStore {
|
|
|
730
730
|
throw new SessionNotFoundError(sessionId);
|
|
731
731
|
return record;
|
|
732
732
|
}
|
|
733
|
-
async readCatalogRecord(sessionId) {
|
|
733
|
+
async readCatalogRecord(sessionId, roleScope = 'ordinary') {
|
|
734
734
|
this.assertOpen();
|
|
735
735
|
assertSafeSessionId(sessionId);
|
|
736
|
-
const role =
|
|
736
|
+
const role = roleScope === 'recoverable'
|
|
737
|
+
? sqliteRecoverableSessionRolePredicate()
|
|
738
|
+
: sqliteOrdinarySessionRolePredicate();
|
|
737
739
|
const row = this.db
|
|
738
740
|
.prepare(`
|
|
739
741
|
SELECT
|
|
@@ -1206,7 +1208,11 @@ export class SqliteSessionMetadataStore {
|
|
|
1206
1208
|
assignment.id !== `wha_${suffix}` ||
|
|
1207
1209
|
assignment.targetMessageId !== `whm_${suffix}` ||
|
|
1208
1210
|
assignment.delegationId !== `whd_${suffix}` ||
|
|
1209
|
-
!
|
|
1211
|
+
!workHubAssignmentAttachmentsMatchTarget(assignment) ||
|
|
1212
|
+
!messageContentsEqual(admission.content, normalizeMessageContent({
|
|
1213
|
+
text: assignment.delegationText ?? assignment.userText,
|
|
1214
|
+
...(assignment.targetAttachments ? { attachments: assignment.targetAttachments } : {}),
|
|
1215
|
+
})) ||
|
|
1210
1216
|
admission.submittedContentDigest !== messageContentDigest(admission.content) ||
|
|
1211
1217
|
admission.submittedPlacement !== 'current_turn' ||
|
|
1212
1218
|
admission.placement !== 'current_turn' ||
|
|
@@ -1825,6 +1831,45 @@ export class SqliteSessionMetadataStore {
|
|
|
1825
1831
|
unique.forEach((messageId, index) => update.run(index, sessionId, messageId));
|
|
1826
1832
|
});
|
|
1827
1833
|
}
|
|
1834
|
+
async readCoordinationTranscriptIndexState() {
|
|
1835
|
+
this.assertOpen();
|
|
1836
|
+
return this.db
|
|
1837
|
+
.prepare(`SELECT (SELECT MAX(sequence) FROM coordination_transcript_index) AS highWater,
|
|
1838
|
+
(SELECT MAX(source_sequence) FROM coordination_transcript_index WHERE source = 'legacy') AS legacy,
|
|
1839
|
+
(SELECT MAX(source_sequence) FROM coordination_transcript_index WHERE source = 'runtime') AS runtime`)
|
|
1840
|
+
.get();
|
|
1841
|
+
}
|
|
1842
|
+
async appendCoordinationTranscriptIndex(records) {
|
|
1843
|
+
this.assertOpen();
|
|
1844
|
+
if (records.length > 64)
|
|
1845
|
+
throw new Error('Coordination transcript index batch exceeds limit');
|
|
1846
|
+
this.transaction(() => {
|
|
1847
|
+
let sequence = this.db
|
|
1848
|
+
.prepare('SELECT MAX(sequence) AS value FROM coordination_transcript_index')
|
|
1849
|
+
.get().value ?? -1;
|
|
1850
|
+
const insert = this.db.prepare(`INSERT INTO coordination_transcript_index
|
|
1851
|
+
(sequence, source, source_sequence) VALUES (?, ?, ?)
|
|
1852
|
+
ON CONFLICT(source, source_sequence) DO NOTHING`);
|
|
1853
|
+
for (const record of records) {
|
|
1854
|
+
if (!Number.isSafeInteger(record.sourceSequence) || record.sourceSequence < 0)
|
|
1855
|
+
throw new Error('Invalid Coordination source sequence');
|
|
1856
|
+
const result = insert.run(sequence + 1, record.source, record.sourceSequence);
|
|
1857
|
+
if (result.changes)
|
|
1858
|
+
sequence++;
|
|
1859
|
+
}
|
|
1860
|
+
});
|
|
1861
|
+
}
|
|
1862
|
+
async readCoordinationTranscriptIndex(request) {
|
|
1863
|
+
this.assertOpen();
|
|
1864
|
+
if (!Number.isSafeInteger(request.limit) || request.limit < 1 || request.limit > 64)
|
|
1865
|
+
throw new Error('Invalid Coordination transcript index limit');
|
|
1866
|
+
const older = request.direction === 'older';
|
|
1867
|
+
return this.db
|
|
1868
|
+
.prepare(`SELECT sequence, source, source_sequence AS sourceSequence
|
|
1869
|
+
FROM coordination_transcript_index WHERE sequence <= ? AND sequence ${older ? '<=' : '>='} ?
|
|
1870
|
+
ORDER BY sequence ${older ? 'DESC' : 'ASC'} LIMIT ?`)
|
|
1871
|
+
.all(request.throughSequence, request.position, request.limit);
|
|
1872
|
+
}
|
|
1828
1873
|
async readMessages(sessionId) {
|
|
1829
1874
|
return this.readMessagesWith(sessionId, decodeStoredMessage);
|
|
1830
1875
|
}
|
|
@@ -4566,6 +4611,21 @@ function isWorkHubActionOperation(value) {
|
|
|
4566
4611
|
value === 'replace' ||
|
|
4567
4612
|
value === 'stop');
|
|
4568
4613
|
}
|
|
4614
|
+
function workHubAssignmentAttachmentsMatchTarget(assignment) {
|
|
4615
|
+
const source = assignment.attachments ?? [];
|
|
4616
|
+
const target = assignment.targetAttachments ?? [];
|
|
4617
|
+
return (source.length === target.length &&
|
|
4618
|
+
source.every((attachment, index) => {
|
|
4619
|
+
const copied = target[index];
|
|
4620
|
+
const { ref: sourceRef, ...sourceMetadata } = attachment;
|
|
4621
|
+
const { ref: targetRef, ...targetMetadata } = copied;
|
|
4622
|
+
return (sourceRef.kind === 'session_file' &&
|
|
4623
|
+
sourceRef.sessionId === WORKHUB_COORDINATION_SESSION_ID &&
|
|
4624
|
+
targetRef.kind === 'session_file' &&
|
|
4625
|
+
targetRef.sessionId === assignment.targetSessionId &&
|
|
4626
|
+
isDeepStrictEqual(sourceMetadata, targetMetadata));
|
|
4627
|
+
}));
|
|
4628
|
+
}
|
|
4569
4629
|
function sameWorkHubAssignmentRequest(existing, requested) {
|
|
4570
4630
|
return isDeepStrictEqual({
|
|
4571
4631
|
actionId: existing.actionId,
|
|
@@ -4574,6 +4634,8 @@ function sameWorkHubAssignmentRequest(existing, requested) {
|
|
|
4574
4634
|
targetSessionId: existing.targetSessionId,
|
|
4575
4635
|
disposition: existing.disposition,
|
|
4576
4636
|
userText: existing.userText,
|
|
4637
|
+
delegationText: existing.delegationText,
|
|
4638
|
+
attachments: existing.attachments,
|
|
4577
4639
|
create: existing.create,
|
|
4578
4640
|
replacesActionId: existing.replacesActionId,
|
|
4579
4641
|
replacesDelegationId: existing.replacesDelegationId,
|
|
@@ -4584,6 +4646,8 @@ function sameWorkHubAssignmentRequest(existing, requested) {
|
|
|
4584
4646
|
targetSessionId: requested.targetSessionId,
|
|
4585
4647
|
disposition: requested.disposition,
|
|
4586
4648
|
userText: requested.userText,
|
|
4649
|
+
delegationText: requested.delegationText,
|
|
4650
|
+
attachments: requested.attachments,
|
|
4587
4651
|
create: requested.create,
|
|
4588
4652
|
replacesActionId: requested.replacesActionId,
|
|
4589
4653
|
replacesDelegationId: requested.replacesDelegationId,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
19
|
import { constants } from 'node:fs';
|
|
20
|
-
import { lstat, open } from 'node:fs/promises';
|
|
20
|
+
import { chmod, lstat, mkdir, open } from 'node:fs/promises';
|
|
21
21
|
import { dirname, isAbsolute, relative, resolve, sep } from 'node:path';
|
|
22
22
|
const openStableFile = open;
|
|
23
23
|
const lstatStableFile = lstat;
|
|
@@ -74,6 +74,19 @@ export async function syncFile(path) {
|
|
|
74
74
|
await handle.close();
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Create (or harden) an owner-only directory: recursive mkdir plus a
|
|
79
|
+
* fail-closed chmod, since mkdir's mode only applies on creation. Callers that
|
|
80
|
+
* store secrets use this before creating their file or update lock.
|
|
81
|
+
*/
|
|
82
|
+
export async function hardenDirectory(dir, mode = 0o700) {
|
|
83
|
+
await mkdir(dir, { recursive: true, mode });
|
|
84
|
+
if (process.platform === 'win32') {
|
|
85
|
+
await chmod(dir, mode).catch(() => { });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
await chmod(dir, mode);
|
|
89
|
+
}
|
|
77
90
|
export async function syncDirectoryChain(path, root, beforeSync) {
|
|
78
91
|
const boundary = resolve(root);
|
|
79
92
|
let current = resolve(path);
|