@atolis-hq/wake 0.4.30 → 0.4.32
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/src/bootstrap/initialise.js +2 -2
- package/dist/src/bootstrap/integration-runtime.js +3 -1
- package/dist/src/bootstrap/version.js +1 -1
- package/dist/src/conversations/contracts/events.js +2 -0
- package/dist/src/conversations/contracts/identifiers.d.ts +1 -0
- package/dist/src/conversations/contracts/identifiers.js +3 -0
- package/dist/src/conversations/contracts/views.d.ts +4 -0
- package/dist/src/integrations/github/application/inbound-translator.d.ts +0 -1
- package/dist/src/integrations/github/application/inbound-translator.js +6 -15
- package/dist/src/orchestration/application/conversation-command-reactor.d.ts +6 -0
- package/dist/src/orchestration/application/conversation-command-reactor.js +59 -0
- package/dist/src/orchestration/index.d.ts +1 -0
- package/dist/src/orchestration/index.js +1 -0
- package/docker/Dockerfile.runtime +1 -1
- package/docker/Dockerfile.runtime.packaged +1 -1
- package/package.json +1 -1
|
@@ -401,7 +401,7 @@ ENV WAKE_MAIN_JS=/app/dist/src/main.js
|
|
|
401
401
|
# Startup ownership repair is best effort because bind-mounted paths or host
|
|
402
402
|
# permissions may prevent chown. The bounded direct-child pass avoids traversing
|
|
403
403
|
# accumulated runtime data.
|
|
404
|
-
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake /wake/workspaces /wake/.wake/auth; chown wake:wake /wake/.wake /wake/workspaces /wake/.wake/auth || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; find /wake/.wake/auth -mindepth 1 -maxdepth 1 -type f -exec chown wake:wake {} + || true; if [ -n \\"
|
|
404
|
+
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake /wake/workspaces /wake/.wake/auth; chown wake:wake /wake/.wake /wake/workspaces /wake/.wake/auth || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; find /wake/.wake/auth -mindepth 1 -maxdepth 1 -type f -exec chown wake:wake {} + || true; if [ -n \\"\${WAKE_HOME_INIT_DIRS:-}\\" ]; then printf '%s\\n' \\"$WAKE_HOME_INIT_DIRS\\" | while IFS= read -r directory; do case \\"$directory\\" in \\"\${WAKE_HOME_INIT_ROOT:-}\\"/*) mkdir -p \\"$directory\\"; chown wake:wake \\"$directory\\" ;; *) exit 1 ;; esac; done; fi; exec su wake -s /bin/sh -c 'HOME=/home/wake exec node \\"$WAKE_MAIN_JS\\" sandbox-entrypoint --wake-root /wake'"]
|
|
405
405
|
`;
|
|
406
406
|
const packagedDockerfile = `# syntax=docker/dockerfile:1
|
|
407
407
|
FROM node:24-bookworm-slim
|
|
@@ -468,7 +468,7 @@ EXPOSE 4317
|
|
|
468
468
|
# Startup ownership repair is best effort because bind-mounted paths or host
|
|
469
469
|
# permissions may prevent chown. The bounded direct-child pass avoids traversing
|
|
470
470
|
# accumulated runtime data.
|
|
471
|
-
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake /wake/workspaces /wake/.wake/auth; chown wake:wake /wake/.wake /wake/workspaces /wake/.wake/auth || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; find /wake/.wake/auth -mindepth 1 -maxdepth 1 -type f -exec chown wake:wake {} + || true; if [ -n \\"
|
|
471
|
+
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake /wake/workspaces /wake/.wake/auth; chown wake:wake /wake/.wake /wake/workspaces /wake/.wake/auth || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; find /wake/.wake/auth -mindepth 1 -maxdepth 1 -type f -exec chown wake:wake {} + || true; if [ -n \\"\${WAKE_HOME_INIT_DIRS:-}\\" ]; then printf '%s\\n' \\"$WAKE_HOME_INIT_DIRS\\" | while IFS= read -r directory; do case \\"$directory\\" in \\"\${WAKE_HOME_INIT_ROOT:-}\\"/*) mkdir -p \\"$directory\\"; chown wake:wake \\"$directory\\" ;; *) exit 1 ;; esac; done; fi; exec su wake -s /bin/sh -c 'HOME=/home/wake exec wake sandbox-entrypoint'"]
|
|
472
472
|
`;
|
|
473
473
|
const userDockerfile = `# User-owned sandbox extension. Wake updates the managed runtime image separately.
|
|
474
474
|
FROM wake-sandbox-runtime:managed
|
|
@@ -4,7 +4,7 @@ import { join } from 'node:path';
|
|
|
4
4
|
import { ControlStreamKind, ScheduleService, createIntakePipeline, createRunnerPipeline, createWorkCancellationPolicy, } from '../control-plane/index.js';
|
|
5
5
|
import { RunRepository, createRuntimeMemoryProfile, } from '../execution/index.js';
|
|
6
6
|
import { AgentRunPublicationReactor, ArtifactRegistrationReactor, DeliveryOutcomeReactor, DeliveryService, IntegrationStreamKind, PollService, ProviderRegistry, deliveryProjectionDefinitions, fakeProviderDefinition, } from '../integrations/index.js';
|
|
7
|
-
import { compileWorkflowSelectors, createPullRequestTransitionEvidence, createResourceTransitionReactor, createWatchReactor, createWatchReconciler, selectWorkflow, workflowName, } from '../orchestration/index.js';
|
|
7
|
+
import { compileWorkflowSelectors, createConversationCommandReactor, createPullRequestTransitionEvidence, createResourceTransitionReactor, createWatchReactor, createWatchReconciler, selectWorkflow, workflowName, } from '../orchestration/index.js';
|
|
8
8
|
import { BuiltInResourceCapability, resourceId, } from '../resources/index.js';
|
|
9
9
|
import { hydrateFakeProviderEvidence } from './fake-provider-files.js';
|
|
10
10
|
import { createRuntimeProjectionSubscriptions, } from './projection-runtime.js';
|
|
@@ -109,6 +109,7 @@ export async function composeIntegrationRuntime(input) {
|
|
|
109
109
|
return operation();
|
|
110
110
|
});
|
|
111
111
|
const outcomes = new DeliveryOutcomeReactor(input.journal, input.orchestration, input.projections, input.processorState, input.conversations, input.subscriptionRunSerialiser);
|
|
112
|
+
const conversationCommands = createConversationCommandReactor(input.orchestration);
|
|
112
113
|
// Only poll hits a rate-limited external API, so only this half of the
|
|
113
114
|
// Tick needs a backing-off host; see bootstrap/surface-cli-applications.ts.
|
|
114
115
|
// Inbound translation reads durable provider facts through its own services,
|
|
@@ -163,6 +164,7 @@ export async function composeIntegrationRuntime(input) {
|
|
|
163
164
|
artifacts.processor,
|
|
164
165
|
outcomes.processor,
|
|
165
166
|
agentRunPublications.processor,
|
|
167
|
+
conversationCommands.processor,
|
|
166
168
|
...providers.map((provider) => provider.inbound.processor),
|
|
167
169
|
],
|
|
168
170
|
providers,
|
|
@@ -124,6 +124,6 @@ export function resolveWakeVersion(options = {}) {
|
|
|
124
124
|
return `g${headHash.slice(0, 7)}`;
|
|
125
125
|
return '0.1.0-dev';
|
|
126
126
|
}
|
|
127
|
-
export const wakeVersion = "v0.4.
|
|
127
|
+
export const wakeVersion = "v0.4.32";
|
|
128
128
|
/** The registry-installable version used by packaged sandbox images. */
|
|
129
129
|
export const wakePackageVersion = packageVersion(defaultRepoRoot) ?? wakeVersion;
|
|
@@ -39,6 +39,8 @@ const origin = z.discriminatedUnion('kind', [
|
|
|
39
39
|
resourceId: z.string().min(1),
|
|
40
40
|
threadId: z.string().min(1),
|
|
41
41
|
messageId: z.string().min(1),
|
|
42
|
+
authorized: z.boolean().optional(),
|
|
43
|
+
capabilities: z.array(z.string().min(1)).readonly().optional(),
|
|
42
44
|
location: z
|
|
43
45
|
.object({
|
|
44
46
|
path: z.string().min(1),
|
|
@@ -3,3 +3,4 @@ import type { WorkItemId } from '../../work/index.js';
|
|
|
3
3
|
export type ConversationId = Brand<string, 'ConversationId'>;
|
|
4
4
|
export declare function conversationId(value: string): ConversationId;
|
|
5
5
|
export declare function conversationIdForWorkItem(workItemId: WorkItemId): ConversationId;
|
|
6
|
+
export declare function workItemIdForConversation(conversationIdValue: ConversationId): WorkItemId;
|
|
@@ -6,3 +6,6 @@ export function conversationId(value) {
|
|
|
6
6
|
export function conversationIdForWorkItem(workItemId) {
|
|
7
7
|
return conversationId(`conversation-${String(workItemId).slice('work-'.length)}`);
|
|
8
8
|
}
|
|
9
|
+
export function workItemIdForConversation(conversationIdValue) {
|
|
10
|
+
return `work-${String(conversationIdValue).slice('conversation-'.length)}`;
|
|
11
|
+
}
|
|
@@ -16,6 +16,10 @@ export type ConversationEntryOrigin = {
|
|
|
16
16
|
readonly resourceId: string;
|
|
17
17
|
readonly threadId: string;
|
|
18
18
|
readonly messageId: string;
|
|
19
|
+
/** Authority established by the receiving surface before the entry is recorded. */
|
|
20
|
+
readonly authorized?: boolean | undefined;
|
|
21
|
+
/** Commands available to this entry's receiving surface. */
|
|
22
|
+
readonly capabilities?: readonly string[] | undefined;
|
|
19
23
|
readonly location?: {
|
|
20
24
|
readonly path: string;
|
|
21
25
|
readonly line: number;
|
|
@@ -75,7 +75,6 @@ export declare class InboundTranslator {
|
|
|
75
75
|
private recordConversationDeferred;
|
|
76
76
|
private appendConversationRecordFact;
|
|
77
77
|
private recordConversationEntry;
|
|
78
|
-
private applyConversationCommand;
|
|
79
78
|
private failureRecorded;
|
|
80
79
|
private retryRecorded;
|
|
81
80
|
private recordTranslationRecovery;
|
|
@@ -110,7 +110,8 @@ export class InboundTranslator {
|
|
|
110
110
|
if (isGitHubAdapterEventType(owned, GitHubEventType.WorkObserved))
|
|
111
111
|
await this.apply(owned);
|
|
112
112
|
if (isGitHubAdapterEventType(owned, GitHubEventType.CommentObserved)) {
|
|
113
|
-
if (
|
|
113
|
+
if (owned.event.payload.reviewKind === 'formal' &&
|
|
114
|
+
!(await this.suppressWorkItemEffects(owned)))
|
|
114
115
|
await applyReviewSignal({
|
|
115
116
|
event: owned,
|
|
116
117
|
journal: this.journal,
|
|
@@ -226,7 +227,7 @@ export class InboundTranslator {
|
|
|
226
227
|
entry.origin.messageId === externalId);
|
|
227
228
|
if (existing?.entries.some((entry) => entry.representations.some((representation) => representation.resourceId === resource.resourceId &&
|
|
228
229
|
representation.externalId === externalId)))
|
|
229
|
-
return
|
|
230
|
+
return;
|
|
230
231
|
if (priorEntry !== undefined) {
|
|
231
232
|
if (priorEntry.body !== event.event.payload.body)
|
|
232
233
|
await this.conversations.revise({
|
|
@@ -234,7 +235,7 @@ export class InboundTranslator {
|
|
|
234
235
|
entryId: priorEntry.entryId,
|
|
235
236
|
body: event.event.payload.body,
|
|
236
237
|
}, commandContext(event));
|
|
237
|
-
return
|
|
238
|
+
return;
|
|
238
239
|
}
|
|
239
240
|
await this.conversations.record({
|
|
240
241
|
conversationId,
|
|
@@ -247,24 +248,14 @@ export class InboundTranslator {
|
|
|
247
248
|
resourceId: resource.resourceId,
|
|
248
249
|
threadId: resource.externalKey.key,
|
|
249
250
|
messageId: externalId,
|
|
251
|
+
authorized: isAuthorizedConversationActor(event),
|
|
252
|
+
capabilities: this.conversationCapabilities,
|
|
250
253
|
...(event.event.payload.reviewKind !== 'issue' ||
|
|
251
254
|
event.event.payload.location === undefined
|
|
252
255
|
? {}
|
|
253
256
|
: { location: event.event.payload.location }),
|
|
254
257
|
},
|
|
255
258
|
}, commandContext(event));
|
|
256
|
-
await this.applyConversationCommand(correlation.workItemId, event);
|
|
257
|
-
}
|
|
258
|
-
async applyConversationCommand(workItemId, event) {
|
|
259
|
-
// Native formal reviews already carry their own verified decision and workflow signal.
|
|
260
|
-
if (event.event.payload.reviewKind !== 'issue')
|
|
261
|
-
return;
|
|
262
|
-
await this.orchestration?.applyConversationCommand(workItemId, {
|
|
263
|
-
body: event.event.payload.body,
|
|
264
|
-
actorId: event.event.payload.actor.id,
|
|
265
|
-
capabilities: this.conversationCapabilities,
|
|
266
|
-
authorized: isAuthorizedConversationActor(event),
|
|
267
|
-
}, commandContext(event));
|
|
268
259
|
}
|
|
269
260
|
async failureRecorded(sourceEventId) {
|
|
270
261
|
return (await this.journal.readStream(integrationStream(this.adapter))).some((event) => {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type EventProcessor } from '@atolis-hq/eventing';
|
|
2
|
+
import type { OrchestrationService } from './orchestration-service.js';
|
|
3
|
+
/** Applies human commands only after their external message is canonical conversation history. */
|
|
4
|
+
export declare function createConversationCommandReactor(orchestration: OrchestrationService): {
|
|
5
|
+
readonly processor: EventProcessor;
|
|
6
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { EventActorKind, EventProcessorCategory, EventProcessorReplayPolicy, correlationId, defineEventProcessor, } from '@atolis-hq/eventing';
|
|
2
|
+
import { ConversationEventType, ConversationOriginKind, selectConversationEvent, workItemIdForConversation, } from '../../conversations/index.js';
|
|
3
|
+
import { conversationCommand } from './conversation-command.js';
|
|
4
|
+
/** Applies human commands only after their external message is canonical conversation history. */
|
|
5
|
+
export function createConversationCommandReactor(orchestration) {
|
|
6
|
+
return {
|
|
7
|
+
processor: defineEventProcessor({
|
|
8
|
+
consumer: 'reactor:orchestration.conversation-command',
|
|
9
|
+
name: 'orchestration.conversation-command',
|
|
10
|
+
owner: 'orchestration',
|
|
11
|
+
category: EventProcessorCategory.Reactor,
|
|
12
|
+
replayPolicy: EventProcessorReplayPolicy.Idempotent,
|
|
13
|
+
select(event) {
|
|
14
|
+
const conversation = selectConversationEvent(event);
|
|
15
|
+
if (conversation?.event.eventType !== ConversationEventType.EntryRecorded)
|
|
16
|
+
return null;
|
|
17
|
+
return {
|
|
18
|
+
conversationId: conversation.stream.id,
|
|
19
|
+
eventId: conversation.event.eventId,
|
|
20
|
+
correlationId: conversation.event.correlationId,
|
|
21
|
+
occurredAt: conversation.event.occurredAt,
|
|
22
|
+
entry: conversation.event.payload,
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
async handle(event) {
|
|
26
|
+
if (event === null)
|
|
27
|
+
return;
|
|
28
|
+
const { entry } = event;
|
|
29
|
+
if (entry.origin.kind !== ConversationOriginKind.External)
|
|
30
|
+
return;
|
|
31
|
+
const workItemId = workItemIdForConversation(event.conversationId);
|
|
32
|
+
const input = {
|
|
33
|
+
body: entry.body,
|
|
34
|
+
actorId: entry.origin.actorId,
|
|
35
|
+
capabilities: (entry.origin.capabilities ?? []),
|
|
36
|
+
authorized: entry.origin.authorized === true,
|
|
37
|
+
};
|
|
38
|
+
await orchestration.applyConversationCommand(workItemId, input, {
|
|
39
|
+
commandId: `${event.eventId}:conversation-command`,
|
|
40
|
+
correlationId: correlationId(event.correlationId),
|
|
41
|
+
occurredAt: event.occurredAt,
|
|
42
|
+
actor: { kind: EventActorKind.Integration, id: entry.origin.adapter },
|
|
43
|
+
});
|
|
44
|
+
if (conversationCommand(entry.body) !== null || !input.authorized)
|
|
45
|
+
return;
|
|
46
|
+
for (const workflow of await orchestration.listAll()) {
|
|
47
|
+
if (workflow.workItemId !== workItemId)
|
|
48
|
+
continue;
|
|
49
|
+
await orchestration.resumeBlockedStageForChanges(workflow.workflowInstanceId, {
|
|
50
|
+
commandId: `${event.eventId}:conversation-reply`,
|
|
51
|
+
correlationId: correlationId(event.correlationId),
|
|
52
|
+
occurredAt: event.occurredAt,
|
|
53
|
+
actor: { kind: EventActorKind.Integration, id: entry.origin.adapter },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -11,6 +11,7 @@ export * from './application/signal-reactor.js';
|
|
|
11
11
|
export * from './application/watch-reactor.js';
|
|
12
12
|
export * from './application/trigger-workflow-instance.js';
|
|
13
13
|
export * from './application/conversation-command.js';
|
|
14
|
+
export * from './application/conversation-command-reactor.js';
|
|
14
15
|
export * from './application/workflow-definition-registry.js';
|
|
15
16
|
export * from './contracts/activity-outcome.js';
|
|
16
17
|
export * from './contracts/commands.js';
|
|
@@ -11,6 +11,7 @@ export * from './application/signal-reactor.js';
|
|
|
11
11
|
export * from './application/watch-reactor.js';
|
|
12
12
|
export * from './application/trigger-workflow-instance.js';
|
|
13
13
|
export * from './application/conversation-command.js';
|
|
14
|
+
export * from './application/conversation-command-reactor.js';
|
|
14
15
|
export * from './application/workflow-definition-registry.js';
|
|
15
16
|
export * from './contracts/activity-outcome.js';
|
|
16
17
|
export * from './contracts/commands.js';
|
|
@@ -84,4 +84,4 @@ ENV WAKE_MAIN_JS=/app/dist/src/main.js
|
|
|
84
84
|
# Startup ownership repair is best effort because bind-mounted paths or host
|
|
85
85
|
# permissions may prevent chown. The bounded direct-child pass avoids traversing
|
|
86
86
|
# accumulated runtime data.
|
|
87
|
-
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake /wake/workspaces /wake/.wake/auth; chown wake:wake /wake/.wake /wake/workspaces /wake/.wake/auth || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; find /wake/.wake/auth -mindepth 1 -maxdepth 1 -type f -exec chown wake:wake {} + || true; if [ -n \"$WAKE_HOME_INIT_DIRS\" ]; then printf '%s\\n' \"$WAKE_HOME_INIT_DIRS\" | while IFS= read -r directory; do case \"$directory\" in \"$WAKE_HOME_INIT_ROOT\"/*) mkdir -p \"$directory\"; chown wake:wake \"$directory\" ;; *) exit 1 ;; esac; done; fi; exec su wake -s /bin/sh -c 'HOME=/home/wake exec node \"$WAKE_MAIN_JS\" sandbox-entrypoint --wake-root /wake'"]
|
|
87
|
+
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake /wake/workspaces /wake/.wake/auth; chown wake:wake /wake/.wake /wake/workspaces /wake/.wake/auth || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; find /wake/.wake/auth -mindepth 1 -maxdepth 1 -type f -exec chown wake:wake {} + || true; if [ -n \"${WAKE_HOME_INIT_DIRS:-}\" ]; then printf '%s\\n' \"$WAKE_HOME_INIT_DIRS\" | while IFS= read -r directory; do case \"$directory\" in \"${WAKE_HOME_INIT_ROOT:-}\"/*) mkdir -p \"$directory\"; chown wake:wake \"$directory\" ;; *) exit 1 ;; esac; done; fi; exec su wake -s /bin/sh -c 'HOME=/home/wake exec node \"$WAKE_MAIN_JS\" sandbox-entrypoint --wake-root /wake'"]
|
|
@@ -65,4 +65,4 @@ EXPOSE 4317
|
|
|
65
65
|
# Startup ownership repair is best effort because bind-mounted paths or host
|
|
66
66
|
# permissions may prevent chown. The bounded direct-child pass avoids traversing
|
|
67
67
|
# accumulated runtime data.
|
|
68
|
-
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake /wake/workspaces /wake/.wake/auth; chown wake:wake /wake/.wake /wake/workspaces /wake/.wake/auth || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; find /wake/.wake/auth -mindepth 1 -maxdepth 1 -type f -exec chown wake:wake {} + || true; if [ -n \"$WAKE_HOME_INIT_DIRS\" ]; then printf '%s\\n' \"$WAKE_HOME_INIT_DIRS\" | while IFS= read -r directory; do case \"$directory\" in \"$WAKE_HOME_INIT_ROOT\"/*) mkdir -p \"$directory\"; chown wake:wake \"$directory\" ;; *) exit 1 ;; esac; done; fi; exec su wake -s /bin/sh -c 'HOME=/home/wake exec wake sandbox-entrypoint --wake-root /wake'"]
|
|
68
|
+
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake /wake/workspaces /wake/.wake/auth; chown wake:wake /wake/.wake /wake/workspaces /wake/.wake/auth || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; find /wake/.wake/auth -mindepth 1 -maxdepth 1 -type f -exec chown wake:wake {} + || true; if [ -n \"${WAKE_HOME_INIT_DIRS:-}\" ]; then printf '%s\\n' \"$WAKE_HOME_INIT_DIRS\" | while IFS= read -r directory; do case \"$directory\" in \"${WAKE_HOME_INIT_ROOT:-}\"/*) mkdir -p \"$directory\"; chown wake:wake \"$directory\" ;; *) exit 1 ;; esac; done; fi; exec su wake -s /bin/sh -c 'HOME=/home/wake exec wake sandbox-entrypoint --wake-root /wake'"]
|