@myagentroam/node 0.9.122 → 0.9.125

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.
Files changed (31) hide show
  1. package/dist/codex-app-server.js +6 -79
  2. package/dist/connector.js +217 -6
  3. package/dist/database.js +2 -1
  4. package/dist/migrations/v014.js +107 -0
  5. package/dist/runner/abstract-runner.js +11 -5
  6. package/dist/runner/claude-code-runner.js +3 -0
  7. package/dist/runner/codex/conversation-parser.js +142 -2
  8. package/dist/runner/codex/managed-run-controller.js +26 -18
  9. package/dist/runner/codex-runner.js +127 -57
  10. package/dist/runner/opencode-runner.js +14 -12
  11. package/dist/runtime-state.js +6 -1
  12. package/dist/service/continuous-run-coordinator.js +8 -2
  13. package/dist/service/conversation-history-service.js +51 -10
  14. package/dist/service/managed-runner-session-service.js +2 -1
  15. package/dist/service/native-session-projection-service.js +3 -1
  16. package/dist/service/node-control-message-service.js +2 -0
  17. package/dist/service/run-event-service.js +18 -2
  18. package/dist/service/runner-interaction-service.js +9 -8
  19. package/dist/service/scheduled-run-authorization-control.js +72 -0
  20. package/dist/service/scheduled-session-coordinator.js +95 -0
  21. package/dist/service/scheduled-session-recurrence.js +72 -0
  22. package/dist/service/scheduled-session-store.js +234 -0
  23. package/dist/service/scheduled-task-mcp-server.js +179 -0
  24. package/dist/service/scheduled-task-mcp-stdio.js +63 -0
  25. package/dist/service/session-catalog-service.js +3 -1
  26. package/dist/service/session-lifecycle-service.js +6 -0
  27. package/dist/service/session-message-service.js +25 -2
  28. package/dist/service/session-presentation-service.js +19 -2
  29. package/dist/service/session-schedule-service.js +153 -0
  30. package/dist/util/runner-native-session-parsers.js +19 -11
  31. package/package.json +4 -4
@@ -0,0 +1,153 @@
1
+ import { assertScheduleTimezone, parseScheduledCron } from './scheduled-session-recurrence.js';
2
+ import { ScheduledSessionStore } from './scheduled-session-store.js';
3
+ export class SessionScheduleService {
4
+ options;
5
+ constructor(options) {
6
+ this.options = options;
7
+ }
8
+ async createFromMcp(input) {
9
+ const session = this.options.runtime.getAgentSession(input.sessionId);
10
+ if (session?.externalSessionId === null)
11
+ await this.options.awaitNativeSessionId(session.id);
12
+ const result = (await this.save({
13
+ sessionId: input.sessionId,
14
+ workspaceId: input.workspaceId,
15
+ __requestUserId: input.actorUserId,
16
+ objective: input.objective,
17
+ cron: input.cron
18
+ }));
19
+ return result.task;
20
+ }
21
+ operations() {
22
+ return {
23
+ 'session.schedule.get': (data) => this.get(record(data)),
24
+ 'session.schedule.save': (data) => this.save(record(data)),
25
+ 'session.schedule.remove': (data) => this.remove(record(data)),
26
+ 'session.schedule.proposal.resolve': (data) => this.resolveProposal(record(data))
27
+ };
28
+ }
29
+ async get(input) {
30
+ const session = await this.requireSession(input);
31
+ const store = this.store();
32
+ const task = store.get(session.externalSessionId ?? session.id);
33
+ return {
34
+ task: task ?? null,
35
+ nodeTimezone: nodeTimezone(),
36
+ canResolveProposals: task !== undefined && input.__requestUserId === task.ownerUserId,
37
+ occurrences: task === undefined ? [] : store.recent(task.id),
38
+ proposals: task === undefined ? [] : store.pendingProposals(task.id)
39
+ };
40
+ }
41
+ async save(input) {
42
+ const session = await this.requireSession(input);
43
+ const actor = input.__requestUserId;
44
+ if (!Number.isSafeInteger(actor) || actor < 1)
45
+ throw new Error('USER_AUTHORIZATION_REQUIRED');
46
+ const sessionId = session.externalSessionId ?? session.id;
47
+ const store = this.store();
48
+ const current = store.get(sessionId);
49
+ const objective = input.objective;
50
+ const cron = input.cron;
51
+ const timezone = current === undefined ? nodeTimezone() : undefined;
52
+ if ((objective !== undefined &&
53
+ (typeof objective !== 'string' ||
54
+ objective.trim().length < 1 ||
55
+ objective.trim().length > 20_000)) ||
56
+ (cron !== undefined && typeof cron !== 'string') ||
57
+ input.timezone !== undefined ||
58
+ (input.enabled !== undefined && typeof input.enabled !== 'boolean'))
59
+ throw new Error('SCHEDULE_INVALID');
60
+ if (typeof cron === 'string')
61
+ parseScheduledCron(cron);
62
+ if (timezone !== undefined)
63
+ assertScheduleTimezone(timezone);
64
+ if (current === undefined &&
65
+ (typeof objective !== 'string' || typeof cron !== 'string' || typeof timezone !== 'string'))
66
+ throw new Error('SCHEDULE_INVALID');
67
+ if (current !== undefined &&
68
+ (!Number.isSafeInteger(input.expectedRevision) || input.expectedRevision !== current.revision))
69
+ throw new Error('SCHEDULE_REVISION_CONFLICT');
70
+ const enabled = input.enabled !== false;
71
+ if ((current === undefined || input.enabled === true) && enabled)
72
+ await this.pin(session);
73
+ else if (current === undefined)
74
+ this.options.database().saveNativeSessionMetadata({
75
+ runner: session.runner,
76
+ externalSessionId: sessionId,
77
+ workspaceId: session.workspaceId
78
+ });
79
+ const task = current === undefined
80
+ ? store.create({
81
+ sessionId,
82
+ workspaceId: session.workspaceId,
83
+ runner: session.runner,
84
+ ownerUserId: actor,
85
+ objective: objective,
86
+ cron: cron,
87
+ timezone: timezone,
88
+ enabled
89
+ })
90
+ : store.update(sessionId, current.revision, {
91
+ ...(objective === undefined ? {} : { objective: objective }),
92
+ ...(cron === undefined ? {} : { cron: cron }),
93
+ ...(timezone === undefined ? {} : { timezone: timezone }),
94
+ ...(input.enabled === undefined ? {} : { enabled: input.enabled })
95
+ });
96
+ const updated = this.options.runtime.getAgentSession(session.id) ?? session;
97
+ this.options.emitSession(updated);
98
+ this.options.changed?.();
99
+ return { task, session: this.options.present(updated) };
100
+ }
101
+ async remove(input) {
102
+ const session = await this.requireSession(input);
103
+ if (!Number.isSafeInteger(input.expectedRevision))
104
+ throw new Error('SCHEDULE_REVISION_CONFLICT');
105
+ this.store().remove(session.externalSessionId ?? session.id, input.expectedRevision);
106
+ this.options.emitSession(session);
107
+ this.options.changed?.();
108
+ return { removed: true, session: this.options.present(session) };
109
+ }
110
+ async resolveProposal(input) {
111
+ const session = await this.requireSession(input);
112
+ if (typeof input.proposalId !== 'string' ||
113
+ typeof input.accept !== 'boolean' ||
114
+ !Number.isSafeInteger(input.__requestUserId))
115
+ throw new Error('SCHEDULE_PROPOSAL_INVALID');
116
+ const task = this.store().resolveProposal(session.externalSessionId ?? session.id, input.proposalId, input.__requestUserId, input.accept);
117
+ this.options.emitSession(session);
118
+ this.options.changed?.();
119
+ return { task, proposals: this.store().pendingProposals(task.id) };
120
+ }
121
+ async requireSession(input) {
122
+ if (typeof input.sessionId !== 'string' || typeof input.workspaceId !== 'string')
123
+ throw new Error('SCHEDULE_INVALID');
124
+ const session = this.options.runtime.getAgentSession(input.sessionId) ??
125
+ (await this.options.projection.createMetadataProjection(input.sessionId));
126
+ if (session === undefined ||
127
+ session.workspaceId !== input.workspaceId ||
128
+ session.externalSessionId === null)
129
+ throw new Error('SESSION_NOT_FOUND');
130
+ return session;
131
+ }
132
+ async pin(session) {
133
+ const sessionId = session.externalSessionId ?? session.id;
134
+ const existing = this.options
135
+ .database()
136
+ .getNativeSessionMetadata(session.runner, sessionId, session.workspaceId);
137
+ if (existing?.pinOrder !== null && existing?.pinOrder !== undefined)
138
+ return;
139
+ await this.options.projection.rename(session, { pinned: true });
140
+ this.options.runtime.updateSessionMetadata({ sessionId: session.id, pinned: true });
141
+ }
142
+ store() {
143
+ return new ScheduledSessionStore(this.options.database().raw);
144
+ }
145
+ }
146
+ function nodeTimezone() {
147
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
148
+ }
149
+ function record(value) {
150
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
151
+ ? value
152
+ : {};
153
+ }
@@ -59,12 +59,11 @@ export function extractCodexThreads(result) {
59
59
  sourceRecord?.kind === 'subagent' ||
60
60
  isPlainRecord(sourceRecord?.subagent) ||
61
61
  isPlainRecord(sourceRecord?.subAgent);
62
- const title = typeof value.name === 'string' && value.name.trim().length > 0
63
- ? value.name.trim()
64
- : typeof value.preview === 'string' && value.preview.trim().length > 0
65
- ? value.preview.trim()
66
- : undefined;
67
- return [{ id: value.id, source, derived, cwd: value.cwd, title }];
62
+ const name = typeof value.name === 'string' ? value.name.trim() : '';
63
+ const preview = typeof value.preview === 'string' ? value.preview.trim() : '';
64
+ const title = name || preview || undefined;
65
+ const titleOrigin = name ? 'OFFICIAL' : preview ? 'PREVIEW' : undefined;
66
+ return [{ id: value.id, source, derived, cwd: value.cwd, title, titleOrigin }];
68
67
  });
69
68
  }
70
69
  export function codexThreadCwd(value) {
@@ -103,13 +102,22 @@ export function claudeDiscoveredSession(value, fallbackCwd) {
103
102
  : undefined;
104
103
  if (externalSessionId === undefined || externalSessionId.length === 0)
105
104
  return undefined;
106
- const title = typeof record.summary === 'string'
105
+ const officialTitle = [record.customTitle, record.firstPrompt, record.title].find((candidate) => typeof candidate === 'string' && candidate.trim().length > 0);
106
+ const previewTitle = typeof record.summary === 'string' && record.summary.trim().length > 0
107
107
  ? record.summary
108
- : typeof record.title === 'string'
109
- ? record.title
110
- : undefined;
108
+ : undefined;
109
+ const title = officialTitle ?? previewTitle;
111
110
  const cwd = typeof record.cwd === 'string' && record.cwd.length > 0 ? record.cwd : fallbackCwd;
112
- return { externalSessionId, cwd, ...(title === undefined ? {} : { title: title.slice(0, 80) }) };
111
+ return {
112
+ externalSessionId,
113
+ cwd,
114
+ ...(title === undefined
115
+ ? {}
116
+ : {
117
+ title: title.slice(0, 80),
118
+ titleOrigin: officialTitle === undefined ? 'PREVIEW' : 'OFFICIAL'
119
+ })
120
+ };
113
121
  }
114
122
  export function latestNativeActivity(history) {
115
123
  return history.lastActivityAt;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.9.122",
3
+ "version": "0.9.125",
4
4
  "description": "MyAgentRoam Node runtime CLI.",
5
5
  "license": "Apache-2.0 AND GPL-2.0-or-later",
6
6
  "type": "module",
@@ -30,9 +30,9 @@
30
30
  "npm": "11.12.1",
31
31
  "ws": "^8.21.3",
32
32
  "zod": "4.4.3",
33
- "@myagentroam/agent": "0.9.122",
34
- "@myagentroam/code-navigation": "0.9.122",
35
- "@myagentroam/protocol": "0.9.122"
33
+ "@myagentroam/agent": "0.9.125",
34
+ "@myagentroam/code-navigation": "0.9.125",
35
+ "@myagentroam/protocol": "0.9.125"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/ws": "^8.18.1"