@danypops/papyrus 0.44.8 → 0.44.10
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/package.json +3 -2
- package/src/{ports → artifact}/artifact-event-reader.ts +1 -1
- package/src/{artifact-relationship-view.ts → artifact/artifact-relationship-view.ts} +3 -3
- package/src/{ports → artifact}/artifact-store.ts +2 -2
- package/src/{artifact-subtree.ts → artifact/artifact-subtree.ts} +4 -4
- package/src/{ports → artifact}/artifact-trash-store.ts +2 -2
- package/src/{adapters → artifact}/sqlite-artifact-scope-store.ts +1 -1
- package/src/{adapters → artifact}/sqlite-artifact-store.ts +15 -15
- package/src/authority-registry.ts +3 -3
- package/src/cli/artifact-command.ts +210 -0
- package/src/cli/discuss-command.ts +256 -0
- package/src/cli/docs-command.ts +192 -0
- package/src/cli/gates-command.ts +46 -0
- package/src/cli/graph-command.ts +171 -0
- package/src/cli/graph-projection-command.ts +79 -0
- package/src/cli/log-command.ts +101 -0
- package/src/cli/migration-command.ts +40 -0
- package/src/cli/note-command.ts +186 -0
- package/src/cli/playbooks-command.ts +313 -0
- package/src/cli/rules-command.ts +220 -0
- package/src/cli/session-identity-command.ts +58 -0
- package/src/cli/shared.ts +5 -0
- package/src/cli/stricli-run.ts +30 -0
- package/src/cli/task-command.ts +892 -0
- package/src/cli.ts +32 -1914
- package/src/client.ts +2 -2
- package/src/{daemon-state.ts → daemon/daemon-state.ts} +1 -1
- package/src/{daemon.ts → daemon/daemon.ts} +3 -3
- package/src/db.ts +1 -1
- package/src/{discussion-service.ts → discussion/discussion-service.ts} +7 -6
- package/src/domain-services.ts +5 -5
- package/src/{graph-projection-service.ts → graph-projection/graph-projection-service.ts} +6 -6
- package/src/{vehicle/artifact-trash-vehicle.ts → handlers/artifact-trash.ts} +4 -4
- package/src/{vehicle/discuss-vehicle.ts → handlers/discuss.ts} +20 -25
- package/src/{vehicle/docs-vehicle.ts → handlers/docs.ts} +4 -37
- package/src/{vehicle/notes-vehicle.ts → handlers/notes.ts} +4 -38
- package/src/{vehicle/playbooks-vehicle.ts → handlers/playbooks.ts} +34 -100
- package/src/{vehicle/papyrus-vehicle.ts → handlers/registry.ts} +16 -16
- package/src/{vehicle/rules-vehicle.ts → handlers/rules.ts} +4 -37
- package/src/{vehicle/artifact-vehicle-shared.ts → handlers/shared.ts} +96 -6
- package/src/{vehicle/tasks-vehicle.ts → handlers/tasks.ts} +37 -124
- package/src/index.ts +13 -13
- package/src/{log-service.ts → log/log-service.ts} +2 -2
- package/src/modules/discuss.ts +7 -28
- package/src/modules/docs.ts +4 -24
- package/src/modules/graph-projection.ts +5 -11
- package/src/modules/logs.ts +2 -23
- package/src/modules/notes.ts +2 -23
- package/src/modules/operation-input.ts +35 -0
- package/src/modules/playbooks.ts +13 -33
- package/src/modules/rules.ts +4 -24
- package/src/modules/session-identity.ts +2 -16
- package/src/modules/tasks.ts +11 -38
- package/src/{note-service.ts → note/note-service.ts} +6 -6
- package/src/ops.ts +6 -6
- package/src/{playbook-definition.ts → playbook/playbook-definition.ts} +7 -6
- package/src/{playbook-execution.ts → playbook/playbook-execution.ts} +5 -4
- package/src/{workflow-execution.ts → playbook/workflow-execution.ts} +12 -12
- package/src/service.ts +30 -57
- package/src/{session-identity-service.ts → session-identity/session-identity-service.ts} +2 -2
- package/src/{ports → stores}/session-identity-store.ts +1 -1
- package/src/{adapters → stores}/sqlite-discussion-round-store.ts +1 -1
- package/src/{adapters → stores}/sqlite-gate-runner.ts +1 -1
- package/src/{adapters → stores}/sqlite-graph-projection-store.ts +1 -1
- package/src/{adapters → stores}/sqlite-log-store.ts +1 -1
- package/src/{adapters → stores}/sqlite-note-event-store.ts +1 -1
- package/src/{adapters → stores}/sqlite-session-identity-store.ts +1 -1
- package/src/{adapters → stores}/sqlite-task-event-store.ts +1 -1
- package/src/{adapters → stores}/sqlite-task-focus-store.ts +1 -1
- package/src/{adapters → stores}/sqlite-task-lease-store.ts +1 -1
- package/src/{adapters → stores}/sqlite-task-scope-store.ts +1 -1
- package/src/{task-context.ts → task/task-context.ts} +4 -4
- package/src/{task-execution.ts → task/task-execution.ts} +1 -1
- package/src/{task-graph-view.ts → task/task-graph-view.ts} +1 -1
- package/src/{task-relationship-view.ts → task/task-relationship-view.ts} +3 -3
- package/src/{task-service.ts → task/task-service.ts} +14 -14
- /package/src/{domain → artifact}/artifact-alias.ts +0 -0
- /package/src/{domain → artifact}/artifact-event.ts +0 -0
- /package/src/{ports → artifact}/artifact-scope-store.ts +0 -0
- /package/src/{domain → artifact}/artifact-trash.ts +0 -0
- /package/src/{domain → artifact}/artifact.ts +0 -0
- /package/src/{ports → artifact}/atomic-artifact-store.ts +0 -0
- /package/src/{log.ts → log/log.ts} +0 -0
- /package/src/{ports → stores}/discussion-round-store.ts +0 -0
- /package/src/{ports → stores}/gate-runner.ts +0 -0
- /package/src/{ports → stores}/graph-projection-store.ts +0 -0
- /package/src/{ports → stores}/graph-renderer.ts +0 -0
- /package/src/{ports → stores}/log-store.ts +0 -0
- /package/src/{ports → stores}/note-event-store.ts +0 -0
- /package/src/{ports → stores}/task-event-store.ts +0 -0
- /package/src/{ports → stores}/task-focus-store.ts +0 -0
- /package/src/{ports → stores}/task-lease-store.ts +0 -0
- /package/src/{ports → stores}/task-scope-store.ts +0 -0
|
@@ -4,23 +4,14 @@
|
|
|
4
4
|
* composition-root-only concern, absent here too). remove/restore/remove_subtree
|
|
5
5
|
* are not duplicated here -- see ./artifact-trash-vehicle.ts.
|
|
6
6
|
*/
|
|
7
|
-
import { bindVehicleOperation, defineVehicleOperation } from "@danypops/vehicle-core";
|
|
8
7
|
import type { VehicleRegistry } from "@danypops/vehicle-server";
|
|
8
|
+
import type { ArtifactScopeStore } from "../artifact/artifact-scope-store.ts";
|
|
9
|
+
import type { ArtifactStore } from "../artifact/artifact-store.ts";
|
|
9
10
|
import { listRules } from "../domain-services.ts";
|
|
10
11
|
import { rulesOperations } from "../modules/rules.ts";
|
|
11
|
-
import
|
|
12
|
-
import type { ArtifactStore } from "../ports/artifact-store.ts";
|
|
13
|
-
import {
|
|
14
|
-
looseObjectSchema,
|
|
15
|
-
numberProp,
|
|
16
|
-
passthroughOutput,
|
|
17
|
-
resolveArtifactIdWidened,
|
|
18
|
-
stringProp,
|
|
19
|
-
validationError,
|
|
20
|
-
} from "./artifact-vehicle-shared.ts";
|
|
12
|
+
import { createOperationDefiner, numberProp, resolveArtifactIdWidened, stringProp, validationError } from "./shared.ts";
|
|
21
13
|
|
|
22
14
|
const OWNER = "rules";
|
|
23
|
-
const LIMITS = { defaultTimeoutMs: 5_000, maxTimeoutMs: 30_000, maxRequestBytes: 65_536, maxResponseBytes: 262_144 };
|
|
24
15
|
|
|
25
16
|
/** Resolves a rule's id from either an explicit id or its title, widened past project_root when unscoped-vs-scoped search finds nothing. */
|
|
26
17
|
function resolveRuleId(
|
|
@@ -54,31 +45,7 @@ function resolveTaskId(artifacts: ArtifactStore, _projectRoot: string | undefine
|
|
|
54
45
|
export function registerRulesVehicleOperations(registry: VehicleRegistry, artifacts: ArtifactStore, scopes: ArtifactScopeStore): void {
|
|
55
46
|
const moduleOperations = new Map(rulesOperations(artifacts, scopes).map((op) => [op.name, op]));
|
|
56
47
|
const call = (name: string, input: Record<string, unknown>): unknown => moduleOperations.get(name)!.execute(input);
|
|
57
|
-
|
|
58
|
-
const define = (
|
|
59
|
-
action: string,
|
|
60
|
-
description: string,
|
|
61
|
-
effect: "read" | "local-write",
|
|
62
|
-
properties: Record<string, { type: string; enum?: readonly string[] }>,
|
|
63
|
-
required: readonly string[],
|
|
64
|
-
resolve: (input: Record<string, unknown>) => Record<string, unknown>,
|
|
65
|
-
): void => {
|
|
66
|
-
const operation = defineVehicleOperation({
|
|
67
|
-
name: `rules.${action}`,
|
|
68
|
-
version: 1,
|
|
69
|
-
description,
|
|
70
|
-
input: looseObjectSchema(properties, required),
|
|
71
|
-
output: passthroughOutput,
|
|
72
|
-
permissions: ["rules:read", "rules:write"],
|
|
73
|
-
effect,
|
|
74
|
-
idempotency: { mode: effect === "read" ? "safe" : "unsafe" },
|
|
75
|
-
limits: LIMITS,
|
|
76
|
-
});
|
|
77
|
-
registry.register(
|
|
78
|
-
OWNER,
|
|
79
|
-
bindVehicleOperation(operation, () => async (context) => call(`rules.${action}`, resolve(context.input))),
|
|
80
|
-
);
|
|
81
|
-
};
|
|
48
|
+
const define = createOperationDefiner(registry, OWNER, "rules", ["rules:read", "rules:write"], call);
|
|
82
49
|
|
|
83
50
|
define(
|
|
84
51
|
"create",
|
|
@@ -3,12 +3,21 @@
|
|
|
3
3
|
* VehicleRegistry projection (notes-vehicle.ts, rules-vehicle.ts, docs-vehicle.ts,
|
|
4
4
|
* artifact-trash-vehicle.ts).
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
import {
|
|
7
|
+
bindVehicleOperation,
|
|
8
|
+
defineVehicleOperation,
|
|
9
|
+
defineVehicleSchema,
|
|
10
|
+
type VehicleContentBlock,
|
|
11
|
+
VehicleError,
|
|
12
|
+
type VehicleOperationContext,
|
|
13
|
+
type VehicleSchemaCodec,
|
|
14
|
+
} from "@danypops/vehicle-core";
|
|
15
|
+
import type { VehicleRegistry } from "@danypops/vehicle-server";
|
|
16
|
+
import type { Artifact } from "../artifact/artifact.ts";
|
|
17
|
+
import type { ArtifactStore } from "../artifact/artifact-store.ts";
|
|
18
|
+
import { PlaybookCompositionError } from "../playbook/playbook-definition.ts";
|
|
19
|
+
import { InvalidSessionSecretError } from "../session-identity/session-identity-service.ts";
|
|
20
|
+
import { TaskDependencyCycleError, TaskExecutionBoundExceededError, type TaskExecutionPlan } from "../task/task-execution.ts";
|
|
12
21
|
|
|
13
22
|
/**
|
|
14
23
|
* VehicleRegistry only ever calls a schema's own safeParse -- jsonSchema is
|
|
@@ -223,3 +232,84 @@ export function buildWorkflowRunContent(
|
|
|
223
232
|
].join("\n");
|
|
224
233
|
return { type: "text", text };
|
|
225
234
|
}
|
|
235
|
+
|
|
236
|
+
export type OperationSchemaProperties = Record<string, { type: string; enum?: readonly string[] }>;
|
|
237
|
+
|
|
238
|
+
export type DefineOperation = (
|
|
239
|
+
action: string,
|
|
240
|
+
description: string,
|
|
241
|
+
effect: "read" | "local-write",
|
|
242
|
+
properties: OperationSchemaProperties,
|
|
243
|
+
required: readonly string[],
|
|
244
|
+
resolve: (input: Record<string, unknown>) => Record<string, unknown>,
|
|
245
|
+
execute?: (input: Record<string, unknown>, context: VehicleOperationContext<Record<string, unknown>>) => unknown,
|
|
246
|
+
) => void;
|
|
247
|
+
|
|
248
|
+
const STANDARD_OPERATION_LIMITS = { defaultTimeoutMs: 5_000, maxTimeoutMs: 30_000, maxRequestBytes: 65_536, maxResponseBytes: 262_144 };
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Every *-vehicle.ts handler wires up the identical defineVehicleOperation +
|
|
252
|
+
* bindVehicleOperation + registry.register triple per action, differing only in
|
|
253
|
+
* owner/domain-prefix/permissions and (for tasks/playbooks) a real execute() override
|
|
254
|
+
* in place of the default "call the wrapped module operation" behavior. One factory,
|
|
255
|
+
* called once per domain, replaces that repetition.
|
|
256
|
+
*/
|
|
257
|
+
export function createOperationDefiner(
|
|
258
|
+
registry: VehicleRegistry,
|
|
259
|
+
owner: string,
|
|
260
|
+
domain: string,
|
|
261
|
+
permissions: readonly [string, string],
|
|
262
|
+
defaultCall: (name: string, input: Record<string, unknown>) => unknown,
|
|
263
|
+
): DefineOperation {
|
|
264
|
+
return (action, description, effect, properties, required, resolve, execute) => {
|
|
265
|
+
const operation = defineVehicleOperation({
|
|
266
|
+
name: `${domain}.${action}`,
|
|
267
|
+
version: 1,
|
|
268
|
+
description,
|
|
269
|
+
input: looseObjectSchema(properties, required),
|
|
270
|
+
output: passthroughOutput,
|
|
271
|
+
permissions: [...permissions],
|
|
272
|
+
effect,
|
|
273
|
+
idempotency: { mode: effect === "read" ? "safe" : "unsafe" },
|
|
274
|
+
limits: STANDARD_OPERATION_LIMITS,
|
|
275
|
+
});
|
|
276
|
+
registry.register(
|
|
277
|
+
owner,
|
|
278
|
+
bindVehicleOperation(
|
|
279
|
+
operation,
|
|
280
|
+
() => async (context) =>
|
|
281
|
+
(execute ?? ((input: Record<string, unknown>) => defaultCall(`${domain}.${action}`, input)))(resolve(context.input), context),
|
|
282
|
+
),
|
|
283
|
+
);
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export interface PairedMutationFieldSpec {
|
|
288
|
+
idProp: string;
|
|
289
|
+
nameProp: string;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* depend/undepend and contain/uncontain (tasks-vehicle.ts, playbooks-vehicle.ts) share
|
|
294
|
+
* one shape: two id-or-name fields resolved the same way for both the add and the
|
|
295
|
+
* remove action, differing only in action name/description. One call replaces two
|
|
296
|
+
* near-identical define() invocations.
|
|
297
|
+
*/
|
|
298
|
+
export function definePairedMutation(
|
|
299
|
+
define: DefineOperation,
|
|
300
|
+
first: PairedMutationFieldSpec,
|
|
301
|
+
second: PairedMutationFieldSpec,
|
|
302
|
+
properties: OperationSchemaProperties,
|
|
303
|
+
required: readonly string[],
|
|
304
|
+
resolveId: (input: Record<string, unknown>, idProp: string, nameProp: string) => string,
|
|
305
|
+
add: { action: string; description: string },
|
|
306
|
+
remove: { action: string; description: string },
|
|
307
|
+
): void {
|
|
308
|
+
const resolve = (input: Record<string, unknown>): Record<string, unknown> => ({
|
|
309
|
+
...input,
|
|
310
|
+
[first.idProp]: resolveId(input, first.idProp, first.nameProp),
|
|
311
|
+
[second.idProp]: resolveId(input, second.idProp, second.nameProp),
|
|
312
|
+
});
|
|
313
|
+
define(add.action, add.description, "local-write", properties, required, resolve);
|
|
314
|
+
define(remove.action, remove.description, "local-write", properties, required, resolve);
|
|
315
|
+
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - session_secret authorizes which session's Task Focus row gets mutated
|
|
11
11
|
* (modules/tasks.ts's own guardFocusMutation). It must never be a model-visible
|
|
12
12
|
* input field -- it travels through VehicleInvocationOptions.principal.claims,
|
|
13
|
-
* the same mechanism playbooks.invoke uses (see vehicle-notes-client.ts).
|
|
13
|
+
* the same mechanism playbooks.invoke uses (see pi-papyrus's tools/vehicle-notes-client.ts).
|
|
14
14
|
* - papyrus.task-focus.v1 is a same-process Pi extension event bus broadcast (a
|
|
15
15
|
* token-cost router or similar can correlate its own telemetry with the
|
|
16
16
|
* currently focused task) with no Vehicle-transport equivalent -- fired from
|
|
@@ -19,26 +19,25 @@
|
|
|
19
19
|
*
|
|
20
20
|
* remove/remove_subtree/restore are not duplicated here -- see ./artifact-trash-vehicle.ts.
|
|
21
21
|
*/
|
|
22
|
-
import { bindVehicleOperation, defineVehicleOperation, type VehicleOperationContext } from "@danypops/vehicle-core";
|
|
23
22
|
import type { VehicleRegistry } from "@danypops/vehicle-server";
|
|
23
|
+
import type { ArtifactStore } from "../artifact/artifact-store.ts";
|
|
24
24
|
import type { TaskViewMode } from "../domain/task-scope.ts";
|
|
25
25
|
import { tasksOperations } from "../modules/tasks.ts";
|
|
26
|
-
import type {
|
|
27
|
-
import type {
|
|
28
|
-
import type {
|
|
29
|
-
import type { TaskCompletion, Tasks } from "../task-service.ts";
|
|
26
|
+
import type { SessionIdentity } from "../session-identity/session-identity-service.ts";
|
|
27
|
+
import type { TaskExecutionPlan } from "../task/task-execution.ts";
|
|
28
|
+
import type { TaskCompletion, Tasks } from "../task/task-service.ts";
|
|
30
29
|
import {
|
|
31
30
|
classifySessionAuthorization,
|
|
32
31
|
classifyTaskDependencyCycles,
|
|
33
32
|
classifyTaskExecutionBounds,
|
|
33
|
+
createOperationDefiner,
|
|
34
|
+
definePairedMutation,
|
|
34
35
|
labelsById,
|
|
35
|
-
looseObjectSchema,
|
|
36
36
|
numberProp,
|
|
37
|
-
passthroughOutput,
|
|
38
37
|
resolveArtifactIdWidened,
|
|
39
38
|
stringProp,
|
|
40
39
|
validationError,
|
|
41
|
-
} from "./
|
|
40
|
+
} from "./shared.ts";
|
|
42
41
|
|
|
43
42
|
const OWNER = "tasks";
|
|
44
43
|
const LIMITS = { defaultTimeoutMs: 5_000, maxTimeoutMs: 30_000, maxRequestBytes: 65_536, maxResponseBytes: 262_144 };
|
|
@@ -167,36 +166,7 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
|
|
|
167
166
|
classifySessionAuthorization(() =>
|
|
168
167
|
classifyTaskExecutionBounds(() => classifyTaskDependencyCycles(() => moduleOperations.get(name)!.execute(input))),
|
|
169
168
|
);
|
|
170
|
-
|
|
171
|
-
const define = (
|
|
172
|
-
action: string,
|
|
173
|
-
description: string,
|
|
174
|
-
effect: "read" | "local-write",
|
|
175
|
-
properties: Record<string, { type: string; enum?: readonly string[] }>,
|
|
176
|
-
required: readonly string[],
|
|
177
|
-
resolve: (input: Record<string, unknown>) => Record<string, unknown>,
|
|
178
|
-
execute?: (input: Record<string, unknown>, context: VehicleOperationContext<Record<string, unknown>>) => unknown,
|
|
179
|
-
): void => {
|
|
180
|
-
const operation = defineVehicleOperation({
|
|
181
|
-
name: `tasks.${action}`,
|
|
182
|
-
version: 1,
|
|
183
|
-
description,
|
|
184
|
-
input: looseObjectSchema(properties, required),
|
|
185
|
-
output: passthroughOutput,
|
|
186
|
-
permissions: ["tasks:read", "tasks:write"],
|
|
187
|
-
effect,
|
|
188
|
-
idempotency: { mode: effect === "read" ? "safe" : "unsafe" },
|
|
189
|
-
limits: LIMITS,
|
|
190
|
-
});
|
|
191
|
-
registry.register(
|
|
192
|
-
OWNER,
|
|
193
|
-
bindVehicleOperation(
|
|
194
|
-
operation,
|
|
195
|
-
() => async (context) =>
|
|
196
|
-
(execute ?? ((input: Record<string, unknown>) => call(`tasks.${action}`, input)))(resolve(context.input), context),
|
|
197
|
-
),
|
|
198
|
-
);
|
|
199
|
-
};
|
|
169
|
+
const define = createOperationDefiner(registry, OWNER, "tasks", ["tasks:read", "tasks:write"], call);
|
|
200
170
|
|
|
201
171
|
/** Shared by every action taking a single id/name: resolves root_task_name first, then name -> id against the final scope. */
|
|
202
172
|
const resolveIdAndScope = (input: Record<string, unknown>): Record<string, unknown> => {
|
|
@@ -395,6 +365,7 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
|
|
|
395
365
|
(input) => input,
|
|
396
366
|
);
|
|
397
367
|
|
|
368
|
+
/** Injects the caller's own session claims (never a model-visible input field) onto every focus/pause/unpause/clear_focus call -- see this file's own doc comment. */
|
|
398
369
|
const focusOperation = (
|
|
399
370
|
action: "focus" | "pause" | "unpause" | "clear_focus",
|
|
400
371
|
description: string,
|
|
@@ -402,24 +373,10 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
|
|
|
402
373
|
required: readonly string[],
|
|
403
374
|
resolve: (input: Record<string, unknown>) => Record<string, unknown>,
|
|
404
375
|
): void => {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
description,
|
|
409
|
-
input: looseObjectSchema(properties, required),
|
|
410
|
-
output: passthroughOutput,
|
|
411
|
-
permissions: ["tasks:read", "tasks:write"],
|
|
412
|
-
effect: "local-write",
|
|
413
|
-
idempotency: { mode: "unsafe" },
|
|
414
|
-
limits: LIMITS,
|
|
376
|
+
define(action, description, "local-write", properties, required, resolve, (resolvedInput, context) => {
|
|
377
|
+
const claims = context.principal?.claims as { sessionId?: string; sessionSecret?: string } | undefined;
|
|
378
|
+
return call(`tasks.${action}`, { ...resolvedInput, session_id: claims?.sessionId, session_secret: claims?.sessionSecret });
|
|
415
379
|
});
|
|
416
|
-
registry.register(
|
|
417
|
-
OWNER,
|
|
418
|
-
bindVehicleOperation(operation, () => async (context) => {
|
|
419
|
-
const claims = context.principal?.claims as { sessionId?: string; sessionSecret?: string } | undefined;
|
|
420
|
-
return call(`tasks.${action}`, { ...resolve(context.input), session_id: claims?.sessionId, session_secret: claims?.sessionSecret });
|
|
421
|
-
}),
|
|
422
|
-
);
|
|
423
380
|
};
|
|
424
381
|
|
|
425
382
|
focusOperation(
|
|
@@ -585,34 +542,16 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
|
|
|
585
542
|
|
|
586
543
|
const scopeProp = { type: "string", enum: ["project", "graph", "all"] } as const;
|
|
587
544
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
id: stringProp,
|
|
594
|
-
name: stringProp,
|
|
595
|
-
dependency_id: stringProp,
|
|
596
|
-
dependency_name: stringProp,
|
|
597
|
-
project_root: stringProp,
|
|
598
|
-
scope: scopeProp,
|
|
599
|
-
session_id: stringProp,
|
|
600
|
-
},
|
|
601
|
-
[],
|
|
602
|
-
(input) => {
|
|
603
|
-
const filter = { projectRoot: input.project_root as string | undefined, scope: input.scope as TaskViewMode | undefined };
|
|
604
|
-
return {
|
|
605
|
-
...input,
|
|
606
|
-
id: resolveTaskId(artifacts, tasks, filter, input.id, input.name),
|
|
607
|
-
dependency_id: resolveTaskId(artifacts, tasks, filter, input.dependency_id, input.dependency_name),
|
|
608
|
-
};
|
|
609
|
-
},
|
|
610
|
-
);
|
|
545
|
+
/** Resolves a task id/name pair against the filter derived from this same input's own project_root/scope -- shared by depend/undepend and contain/uncontain below. */
|
|
546
|
+
const resolveScopedTaskId = (input: Record<string, unknown>, idProp: string, nameProp: string): string => {
|
|
547
|
+
const filter = { projectRoot: input.project_root as string | undefined, scope: input.scope as TaskViewMode | undefined };
|
|
548
|
+
return resolveTaskId(artifacts, tasks, filter, input[idProp], input[nameProp]);
|
|
549
|
+
};
|
|
611
550
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
"
|
|
551
|
+
definePairedMutation(
|
|
552
|
+
define,
|
|
553
|
+
{ idProp: "id", nameProp: "name" },
|
|
554
|
+
{ idProp: "dependency_id", nameProp: "dependency_name" },
|
|
616
555
|
{
|
|
617
556
|
id: stringProp,
|
|
618
557
|
name: stringProp,
|
|
@@ -623,44 +562,19 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
|
|
|
623
562
|
session_id: stringProp,
|
|
624
563
|
},
|
|
625
564
|
[],
|
|
626
|
-
|
|
627
|
-
const filter = { projectRoot: input.project_root as string | undefined, scope: input.scope as TaskViewMode | undefined };
|
|
628
|
-
return {
|
|
629
|
-
...input,
|
|
630
|
-
id: resolveTaskId(artifacts, tasks, filter, input.id, input.name),
|
|
631
|
-
dependency_id: resolveTaskId(artifacts, tasks, filter, input.dependency_id, input.dependency_name),
|
|
632
|
-
};
|
|
633
|
-
},
|
|
634
|
-
);
|
|
635
|
-
|
|
636
|
-
define(
|
|
637
|
-
"contain",
|
|
638
|
-
"Nests a child Task inside a parent (parent_id/parent_name contains child_id/child_name) -- explicit hierarchy, distinct from depends_on execution ordering. A name resolved outside project_root's own scope is retried once against every project before failing, unless scope is pinned explicitly.",
|
|
639
|
-
"local-write",
|
|
565
|
+
resolveScopedTaskId,
|
|
640
566
|
{
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
child_name: stringProp,
|
|
645
|
-
project_root: stringProp,
|
|
646
|
-
scope: scopeProp,
|
|
647
|
-
session_id: stringProp,
|
|
648
|
-
},
|
|
649
|
-
[],
|
|
650
|
-
(input) => {
|
|
651
|
-
const filter = { projectRoot: input.project_root as string | undefined, scope: input.scope as TaskViewMode | undefined };
|
|
652
|
-
return {
|
|
653
|
-
...input,
|
|
654
|
-
parent_id: resolveTaskId(artifacts, tasks, filter, input.parent_id, input.parent_name),
|
|
655
|
-
child_id: resolveTaskId(artifacts, tasks, filter, input.child_id, input.child_name),
|
|
656
|
-
};
|
|
567
|
+
action: "depend",
|
|
568
|
+
description:
|
|
569
|
+
"Adds a dependency edge (this task waits for dependency_id/dependency_name). Dependency edges form an executable DAG -- self-dependencies and cycles are rejected. A name resolved outside project_root's own scope is retried once against every project before failing, unless scope is pinned explicitly.",
|
|
657
570
|
},
|
|
571
|
+
{ action: "undepend", description: "Removes a dependency edge. Idempotent -- a no-op if the edge is already absent." },
|
|
658
572
|
);
|
|
659
573
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
"
|
|
574
|
+
definePairedMutation(
|
|
575
|
+
define,
|
|
576
|
+
{ idProp: "parent_id", nameProp: "parent_name" },
|
|
577
|
+
{ idProp: "child_id", nameProp: "child_name" },
|
|
664
578
|
{
|
|
665
579
|
parent_id: stringProp,
|
|
666
580
|
parent_name: stringProp,
|
|
@@ -671,14 +585,13 @@ export function registerTasksVehicleOperations(registry: VehicleRegistry, deps:
|
|
|
671
585
|
session_id: stringProp,
|
|
672
586
|
},
|
|
673
587
|
[],
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
child_id: resolveTaskId(artifacts, tasks, filter, input.child_id, input.child_name),
|
|
680
|
-
};
|
|
588
|
+
resolveScopedTaskId,
|
|
589
|
+
{
|
|
590
|
+
action: "contain",
|
|
591
|
+
description:
|
|
592
|
+
"Nests a child Task inside a parent (parent_id/parent_name contains child_id/child_name) -- explicit hierarchy, distinct from depends_on execution ordering. A name resolved outside project_root's own scope is retried once against every project before failing, unless scope is pinned explicitly.",
|
|
681
593
|
},
|
|
594
|
+
{ action: "uncontain", description: "Removes a parent/child nesting. Idempotent -- a no-op if the edge is already absent." },
|
|
682
595
|
);
|
|
683
596
|
|
|
684
597
|
define(
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* purposeful API, not "everything happens to be reachable."
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
export {
|
|
8
|
+
export type { Artifact, ArtifactEdge } from "./artifact/artifact.ts";
|
|
9
|
+
export { projectArtifactRelationships } from "./artifact/artifact-relationship-view.ts";
|
|
10
|
+
export type { ArtifactStore } from "./artifact/artifact-store.ts";
|
|
9
11
|
export {
|
|
10
12
|
connectPapyrusClient,
|
|
11
13
|
type PapyrusClient,
|
|
@@ -15,8 +17,7 @@ export {
|
|
|
15
17
|
type VehicleClientTarget,
|
|
16
18
|
} from "./client.ts";
|
|
17
19
|
export * from "./constants.ts";
|
|
18
|
-
export type { DiscussionAndRounds } from "./discussion-service.ts";
|
|
19
|
-
export type { Artifact, ArtifactEdge } from "./domain/artifact.ts";
|
|
20
|
+
export type { DiscussionAndRounds } from "./discussion/discussion-service.ts";
|
|
20
21
|
export { checklistEntries, PROOF_TYPES, type ProofReference } from "./domain/checklist.ts";
|
|
21
22
|
export { DISCUSSION_SUBTYPE, type DiscussionRound, readDiscussionExtra } from "./domain/discussion.ts";
|
|
22
23
|
export type { DisplayGraph, DisplayGraphEdge, DisplayGraphNode, RenderedGraph } from "./domain/display-graph.ts";
|
|
@@ -25,14 +26,13 @@ export type { NoteHistoryPage } from "./domain/note-event.ts";
|
|
|
25
26
|
export type { TaskEvent, TaskHistoryPage } from "./domain/task-event.ts";
|
|
26
27
|
export type { TaskLease } from "./domain/task-lease.ts";
|
|
27
28
|
export type { TaskViewSelection } from "./domain/task-scope.ts";
|
|
28
|
-
export { NOTE_DISPOSITIONS } from "./note-service.ts";
|
|
29
|
-
export type { PlaybookInvocationResult, PlaybookMissingArguments } from "./playbook-execution.ts";
|
|
30
|
-
export type {
|
|
31
|
-
export type { GraphRenderer } from "./ports/graph-renderer.ts";
|
|
29
|
+
export { NOTE_DISPOSITIONS } from "./note/note-service.ts";
|
|
30
|
+
export type { PlaybookInvocationResult, PlaybookMissingArguments } from "./playbook/playbook-execution.ts";
|
|
31
|
+
export type { WorkflowRunResult } from "./playbook/workflow-execution.ts";
|
|
32
32
|
export type { OperationName, SchemaState } from "./service.ts";
|
|
33
|
-
export {
|
|
34
|
-
export {
|
|
35
|
-
export {
|
|
36
|
-
export {
|
|
37
|
-
export
|
|
38
|
-
export type {
|
|
33
|
+
export type { GraphRenderer } from "./stores/graph-renderer.ts";
|
|
34
|
+
export { taskContext } from "./task/task-context.ts";
|
|
35
|
+
export { projectTaskExecution, type TaskExecutionPlan, type TaskExecutionState } from "./task/task-execution.ts";
|
|
36
|
+
export { projectTaskGraph, type TaskGraphView } from "./task/task-graph-view.ts";
|
|
37
|
+
export { fallbackLabel, projectTaskRelationships } from "./task/task-relationship-view.ts";
|
|
38
|
+
export type { TaskCompletion, TaskGraph, TaskNode, TaskStatus } from "./task/task-service.ts";
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
type LogQuery,
|
|
10
10
|
meetsLevel,
|
|
11
11
|
validateAppendLogEntryCommand,
|
|
12
|
-
} from "
|
|
13
|
-
import type { LogStore } from "
|
|
12
|
+
} from "../domain/log-entry.ts";
|
|
13
|
+
import type { LogStore } from "../stores/log-store.ts";
|
|
14
14
|
|
|
15
15
|
export class Logs {
|
|
16
16
|
constructor(private readonly store: LogStore) {}
|
package/src/modules/discuss.ts
CHANGED
|
@@ -3,44 +3,23 @@
|
|
|
3
3
|
* domain/discussion.ts and discussion-service.ts for the full design.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
7
|
-
import type {
|
|
6
|
+
import type { ArtifactEventContext } from "../artifact/artifact-event.ts";
|
|
7
|
+
import type { Discussions } from "../discussion/discussion-service.ts";
|
|
8
8
|
import type { OperationDefinition } from "../module-registry.ts";
|
|
9
|
+
import { type OperationInput, optionalNumber, optionalString, optionalStringArray } from "./operation-input.ts";
|
|
9
10
|
|
|
10
11
|
const MODULE_ID = "discuss";
|
|
11
12
|
|
|
12
|
-
type OperationInput = Record<string, unknown>;
|
|
13
|
-
|
|
14
|
-
function string(input: OperationInput, key: string): string {
|
|
15
|
-
const value = input[key];
|
|
16
|
-
if (typeof value !== "string" || value.length === 0)
|
|
17
|
-
throw new Error(`${key} is required${REQUIRED_FIELD_HINTS[key] ? ` (${REQUIRED_FIELD_HINTS[key]})` : ""}`);
|
|
18
|
-
return value;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
13
|
/** Fields whose name alone doesn't say what a valid value looks like -- everything else (title, content, id, settlement) is self-explanatory. */
|
|
22
14
|
const REQUIRED_FIELD_HINTS: Record<string, string> = {
|
|
23
15
|
actor: 'a display name for who is posting, e.g. "alice" or "agent"',
|
|
24
16
|
};
|
|
25
17
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (value === undefined) return undefined;
|
|
29
|
-
if (typeof value !== "string") throw new Error(`${key} must be a string`);
|
|
30
|
-
return value;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function optionalStringArray(input: OperationInput, key: string): string[] | undefined {
|
|
34
|
-
const value = input[key];
|
|
35
|
-
if (value === undefined) return undefined;
|
|
36
|
-
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(`${key} must be an array of strings`);
|
|
37
|
-
return value as string[];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function optionalNumber(input: OperationInput, key: string): number | undefined {
|
|
18
|
+
/** Same contract as the shared string() in ./operation-input.ts, plus a field-specific hint on failure -- discuss's own operations are the one surface where a bare "actor is required" isn't self-explanatory. */
|
|
19
|
+
function string(input: OperationInput, key: string): string {
|
|
41
20
|
const value = input[key];
|
|
42
|
-
if (value ===
|
|
43
|
-
|
|
21
|
+
if (typeof value !== "string" || value.length === 0)
|
|
22
|
+
throw new Error(`${key} is required${REQUIRED_FIELD_HINTS[key] ? ` (${REQUIRED_FIELD_HINTS[key]})` : ""}`);
|
|
44
23
|
return value;
|
|
45
24
|
}
|
|
46
25
|
|
package/src/modules/docs.ts
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* Imports only src/domain-services.ts's Doc functions, which are already generic
|
|
7
7
|
* ArtifactStore-based with no other module's concrete class dependency.
|
|
8
8
|
*/
|
|
9
|
+
|
|
10
|
+
import type { ArtifactScopeStore } from "../artifact/artifact-scope-store.ts";
|
|
11
|
+
import type { ArtifactStore } from "../artifact/artifact-store.ts";
|
|
9
12
|
import type { AuthorityRegistry } from "../authority-registry.ts";
|
|
10
13
|
import {
|
|
11
14
|
assignDocumentProject,
|
|
@@ -18,33 +21,10 @@ import {
|
|
|
18
21
|
updateDocument,
|
|
19
22
|
} from "../domain-services.ts";
|
|
20
23
|
import type { OperationDefinition } from "../module-registry.ts";
|
|
21
|
-
import type
|
|
22
|
-
import type { ArtifactStore } from "../ports/artifact-store.ts";
|
|
24
|
+
import { type OperationInput, optionalNumber, optionalString, string } from "./operation-input.ts";
|
|
23
25
|
|
|
24
26
|
const MODULE_ID = "docs";
|
|
25
27
|
|
|
26
|
-
type OperationInput = Record<string, unknown>;
|
|
27
|
-
|
|
28
|
-
function string(input: OperationInput, key: string): string {
|
|
29
|
-
const value = input[key];
|
|
30
|
-
if (typeof value !== "string" || value.length === 0) throw new Error(`${key} is required`);
|
|
31
|
-
return value;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function optionalString(input: OperationInput, key: string): string | undefined {
|
|
35
|
-
const value = input[key];
|
|
36
|
-
if (value === undefined) return undefined;
|
|
37
|
-
if (typeof value !== "string") throw new Error(`${key} must be a string`);
|
|
38
|
-
return value;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function optionalNumber(input: OperationInput, key: string): number | undefined {
|
|
42
|
-
const value = input[key];
|
|
43
|
-
if (value === undefined) return undefined;
|
|
44
|
-
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${key} must be a number`);
|
|
45
|
-
return value;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
28
|
const eventContext = (input: OperationInput) => ({
|
|
49
29
|
actor: optionalString(input, "actor"),
|
|
50
30
|
source: optionalString(input, "source"),
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* src/domain/graph-projection.ts and src/graph-projection-service.ts for the protocol
|
|
6
6
|
* itself; this file only parses/validates raw operation input into that typed shape.
|
|
7
7
|
*/
|
|
8
|
+
|
|
9
|
+
import type { ArtifactStore } from "../artifact/artifact-store.ts";
|
|
8
10
|
import type { AuthorityRegistry } from "../authority-registry.ts";
|
|
9
11
|
import type {
|
|
10
12
|
GRAPH_PROJECTION_SCHEMA_VERSION,
|
|
@@ -12,21 +14,13 @@ import type {
|
|
|
12
14
|
ProjectedArtifact,
|
|
13
15
|
ProjectedEdge,
|
|
14
16
|
} from "../domain/graph-projection.ts";
|
|
15
|
-
import { GraphProjection } from "../graph-projection-service.ts";
|
|
17
|
+
import { GraphProjection } from "../graph-projection/graph-projection-service.ts";
|
|
16
18
|
import type { OperationDefinition } from "../module-registry.ts";
|
|
17
|
-
import type {
|
|
18
|
-
import type
|
|
19
|
+
import type { GraphProjectionStore } from "../stores/graph-projection-store.ts";
|
|
20
|
+
import { type OperationInput, string } from "./operation-input.ts";
|
|
19
21
|
|
|
20
22
|
const MODULE_ID = "graph_projection";
|
|
21
23
|
|
|
22
|
-
type OperationInput = Record<string, unknown>;
|
|
23
|
-
|
|
24
|
-
function string(input: OperationInput, key: string): string {
|
|
25
|
-
const value = input[key];
|
|
26
|
-
if (typeof value !== "string" || value.length === 0) throw new Error(`${key} is required`);
|
|
27
|
-
return value;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
24
|
function number(input: OperationInput, key: string): number {
|
|
31
25
|
const value = input[key];
|
|
32
26
|
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${key} is required and must be a number`);
|
package/src/modules/logs.ts
CHANGED
|
@@ -6,33 +6,12 @@
|
|
|
6
6
|
* comment on why `log` is not an Artifact kind).
|
|
7
7
|
*/
|
|
8
8
|
import type { JsonValue, LogLevel } from "../domain/log-entry.ts";
|
|
9
|
-
import type { Logs } from "../log-service.ts";
|
|
9
|
+
import type { Logs } from "../log/log-service.ts";
|
|
10
10
|
import type { OperationDefinition } from "../module-registry.ts";
|
|
11
|
+
import { type OperationInput, optionalNumber, optionalString, string } from "./operation-input.ts";
|
|
11
12
|
|
|
12
13
|
const MODULE_ID = "logs";
|
|
13
14
|
|
|
14
|
-
type OperationInput = Record<string, unknown>;
|
|
15
|
-
|
|
16
|
-
function string(input: OperationInput, key: string): string {
|
|
17
|
-
const value = input[key];
|
|
18
|
-
if (typeof value !== "string" || value.length === 0) throw new Error(`${key} is required`);
|
|
19
|
-
return value;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function optionalString(input: OperationInput, key: string): string | undefined {
|
|
23
|
-
const value = input[key];
|
|
24
|
-
if (value === undefined) return undefined;
|
|
25
|
-
if (typeof value !== "string") throw new Error(`${key} must be a string`);
|
|
26
|
-
return value;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function optionalNumber(input: OperationInput, key: string): number | undefined {
|
|
30
|
-
const value = input[key];
|
|
31
|
-
if (value === undefined) return undefined;
|
|
32
|
-
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${key} must be a number`);
|
|
33
|
-
return value;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
15
|
function isJsonValue(value: unknown): value is JsonValue {
|
|
37
16
|
return (
|
|
38
17
|
value === null ||
|