@myagentroam/node 0.9.128 → 0.9.130

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/connector.js CHANGED
@@ -1334,7 +1334,7 @@ export class NodeConnector {
1334
1334
  session.workspaceId !== task.workspaceId ||
1335
1335
  session.runner !== task.runner)
1336
1336
  throw new Error('SCHEDULE_SESSION_NOT_FOUND');
1337
- const configuration = this.nativeSessionProjectionService.resumedConfiguration(session);
1337
+ const configuredSession = this.nativeSessionProjectionService.ensureResumedConfiguration(session);
1338
1338
  return this.scheduledRunAuthorization.authorize({
1339
1339
  taskId: task.id,
1340
1340
  revision: task.revision,
@@ -1343,7 +1343,7 @@ export class NodeConnector {
1343
1343
  workspaceId: task.workspaceId,
1344
1344
  ownerUserId: task.ownerUserId,
1345
1345
  runner: task.runner,
1346
- model: configuration.model
1346
+ model: configuredSession.model
1347
1347
  });
1348
1348
  },
1349
1349
  submit: async (task, occurrenceId, context) => {
@@ -98,31 +98,33 @@ export class NativeSessionProjectionService {
98
98
  externalSessionId: direct.externalSessionId,
99
99
  nativeControl: 'EXTERNAL',
100
100
  cwd: direct.cwd,
101
+ ...(direct.model === null ? {} : { model: direct.model }),
102
+ ...(direct.effort === null ? {} : { effort: direct.effort }),
103
+ ...(direct.access === null ? {} : { access: direct.access }),
101
104
  ...(direct.runnerTitle === null ? {} : { title: direct.runnerTitle }),
102
105
  ...(direct.customTitle === null ? {} : { customTitle: direct.customTitle })
103
106
  });
104
107
  }
105
108
  resumedConfiguration(session) {
106
109
  const workspace = this.options.database()?.getWorkspace(session.workspaceId);
107
- const current = {
108
- runner: session.runner,
109
- model: session.model,
110
- effort: session.effort,
111
- access: session.access
112
- };
113
- if (typeof current.model === 'string' &&
114
- typeof current.effort === 'string' &&
115
- typeof current.access === 'string')
116
- return session.runner === 'mar-agent'
117
- ? { ...current, access: 'full-access' }
118
- : current;
119
110
  const saved = this.options
120
111
  .database()
121
112
  ?.getRunnerDefaults()
122
113
  .find((value) => value.runner === session.runner);
123
- if (saved !== undefined)
124
- return session.runner === 'mar-agent' ? { ...saved, access: 'full-access' } : saved;
125
- return this.options.runners.require(session.runner).defaultConfiguration(workspace);
114
+ const fallback = saved ?? this.options.runners.require(session.runner).defaultConfiguration(workspace);
115
+ const configuration = {
116
+ runner: session.runner,
117
+ model: typeof session.model === 'string' && session.model.trim().length > 0
118
+ ? session.model
119
+ : fallback.model,
120
+ effort: typeof session.effort === 'string' ? session.effort : fallback.effort,
121
+ access: typeof session.access === 'string' && session.access.trim().length > 0
122
+ ? session.access
123
+ : fallback.access
124
+ };
125
+ return session.runner === 'mar-agent'
126
+ ? { ...configuration, access: 'full-access' }
127
+ : configuration;
126
128
  }
127
129
  ensureResumedConfiguration(session) {
128
130
  const configuration = this.resumedConfiguration(session);
@@ -1,14 +1,21 @@
1
1
  export function parseScheduledCron(cron) {
2
- const fields = cron.trim().split(/\s+/);
3
- if (fields.length !== 5 || fields[2] !== '*' || fields[3] !== '*')
4
- throw new Error('SCHEDULE_CRON_INVALID');
5
- const minute = boundedInteger(fields[0], 0, 59);
6
- if (fields[1] === '*' && fields[4] === '*')
7
- return { kind: 'HOURLY', minute };
8
- const hour = boundedInteger(fields[1], 0, 23);
9
- if (fields[4] === '*')
10
- return { kind: 'DAILY', minute, hour };
11
- return { kind: 'WEEKLY', minute, hour, weekday: boundedInteger(fields[4], 0, 6) };
2
+ const parsed = parseCronExpression(cron);
3
+ if (parsed.dayOfMonthWildcard &&
4
+ parsed.monthWildcard &&
5
+ /^\d+$/.test(parsed.fields[0] ?? '') &&
6
+ (parsed.fields[1] === '*' || /^\d+$/.test(parsed.fields[1] ?? '')) &&
7
+ (parsed.fields[4] === '*' || /^\d+$/.test(parsed.fields[4] ?? ''))) {
8
+ const minute = exactInteger(parsed.fields[0], 0, 59);
9
+ const hour = parsed.fields[1] === '*' ? undefined : exactInteger(parsed.fields[1], 0, 23);
10
+ const weekdayValue = parsed.fields[4] === '*' ? undefined : exactInteger(parsed.fields[4], 0, 7);
11
+ if (hour === undefined && weekdayValue === undefined)
12
+ return { kind: 'HOURLY', minute };
13
+ if (hour !== undefined && weekdayValue === undefined)
14
+ return { kind: 'DAILY', minute, hour };
15
+ if (hour !== undefined && weekdayValue !== undefined)
16
+ return { kind: 'WEEKLY', minute, hour, weekday: weekdayValue === 7 ? 0 : weekdayValue };
17
+ }
18
+ return { kind: 'CRON', expression: parsed.expression };
12
19
  }
13
20
  export function assertScheduleTimezone(timezone) {
14
21
  if (timezone.length < 1 || timezone.length > 128)
@@ -21,7 +28,7 @@ export function assertScheduleTimezone(timezone) {
21
28
  }
22
29
  }
23
30
  export function nextScheduledAt(cron, timezone, after) {
24
- const cadence = parseScheduledCron(cron);
31
+ const schedule = parseCronExpression(cron);
25
32
  assertScheduleTimezone(timezone);
26
33
  if (!Number.isSafeInteger(after) || after < 0)
27
34
  throw new Error('SCHEDULE_TIME_INVALID');
@@ -37,17 +44,81 @@ export function nextScheduledAt(cron, timezone, after) {
37
44
  });
38
45
  const previousKey = localKey(formatter, after);
39
46
  let candidate = Math.floor(after / 60_000) * 60_000 + 60_000;
40
- for (let index = 0; index < 8 * 24 * 60 + 120; index++, candidate += 60_000) {
47
+ for (let index = 0; index < 8 * 366 * 24 * 60; index++, candidate += 60_000) {
41
48
  const local = localParts(formatter, candidate);
42
- if (local.minute === cadence.minute &&
43
- (cadence.kind === 'HOURLY' || local.hour === cadence.hour) &&
44
- (cadence.kind !== 'WEEKLY' || local.weekday === cadence.weekday) &&
45
- local.key !== previousKey)
49
+ if (matches(schedule, local) && local.key !== previousKey)
46
50
  return candidate;
47
51
  }
48
52
  throw new Error('SCHEDULE_NEXT_TIME_UNAVAILABLE');
49
53
  }
50
- function boundedInteger(value, min, max) {
54
+ function parseCronExpression(cron) {
55
+ const expression = cron.trim();
56
+ const fields = expression.split(/\s+/);
57
+ if (expression.length === 0 ||
58
+ expression.length > 64 ||
59
+ fields.length !== 5 ||
60
+ fields.some((field) => field.length === 0))
61
+ throw new Error('SCHEDULE_CRON_INVALID');
62
+ const minute = parseField(fields[0] ?? '', 0, 59);
63
+ const hour = parseField(fields[1] ?? '', 0, 23);
64
+ const dayOfMonth = parseField(fields[2] ?? '', 1, 31);
65
+ const month = parseField(fields[3] ?? '', 1, 12);
66
+ const dayOfWeek = parseField(fields[4] ?? '', 0, 7, (value) => (value === 7 ? 0 : value));
67
+ return {
68
+ expression,
69
+ fields,
70
+ minute: minute.values,
71
+ hour: hour.values,
72
+ dayOfMonth: dayOfMonth.values,
73
+ month: month.values,
74
+ dayOfWeek: dayOfWeek.values,
75
+ dayOfMonthWildcard: minuteWildcard(dayOfMonth.values, 1, 31),
76
+ monthWildcard: minuteWildcard(month.values, 1, 12),
77
+ dayOfWeekWildcard: minuteWildcard(dayOfWeek.values, 0, 6)
78
+ };
79
+ }
80
+ function parseField(field, min, max, normalize = (value) => value) {
81
+ if (field.includes('?'))
82
+ throw new Error('SCHEDULE_CRON_INVALID');
83
+ const values = new Set();
84
+ for (const segment of field.split(',')) {
85
+ if (segment.length === 0)
86
+ throw new Error('SCHEDULE_CRON_INVALID');
87
+ const parts = segment.split('/');
88
+ if (parts.length > 2 || parts.some((part) => part.length === 0))
89
+ throw new Error('SCHEDULE_CRON_INVALID');
90
+ const base = parts[0] ?? '';
91
+ const step = parts[1] === undefined ? 1 : exactInteger(parts[1], 1, max - min + 1);
92
+ let start;
93
+ let end;
94
+ if (base === '*') {
95
+ start = min;
96
+ end = max;
97
+ }
98
+ else if (/^\d+-\d+$/.test(base)) {
99
+ const range = base.split('-');
100
+ start = exactInteger(range[0], min, max);
101
+ end = exactInteger(range[1], min, max);
102
+ if (start > end)
103
+ throw new Error('SCHEDULE_CRON_INVALID');
104
+ }
105
+ else if (parts.length === 1 && /^\d+$/.test(base)) {
106
+ start = exactInteger(base, min, max);
107
+ end = start;
108
+ }
109
+ else {
110
+ throw new Error('SCHEDULE_CRON_INVALID');
111
+ }
112
+ if (parts[1] !== undefined && start === end)
113
+ throw new Error('SCHEDULE_CRON_INVALID');
114
+ for (let value = start; value <= end; value += step)
115
+ values.add(normalize(value));
116
+ }
117
+ if (values.size === 0)
118
+ throw new Error('SCHEDULE_CRON_INVALID');
119
+ return { values };
120
+ }
121
+ function exactInteger(value, min, max) {
51
122
  if (value === undefined || !/^(0|[1-9][0-9]*)$/.test(value))
52
123
  throw new Error('SCHEDULE_CRON_INVALID');
53
124
  const number = Number(value);
@@ -55,18 +126,41 @@ function boundedInteger(value, min, max) {
55
126
  throw new Error('SCHEDULE_CRON_INVALID');
56
127
  return number;
57
128
  }
129
+ function minuteWildcard(values, min, max) {
130
+ return values.size === max - min + 1;
131
+ }
58
132
  function localParts(formatter, time) {
59
133
  const fields = Object.fromEntries(formatter.formatToParts(time).map((part) => [part.type, part.value]));
60
134
  const hour = Number(fields['hour']);
61
135
  const minute = Number(fields['minute']);
136
+ const day = Number(fields['day']);
137
+ const month = Number(fields['month']);
62
138
  const weekday = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].indexOf(fields['weekday'] ?? '');
63
139
  return {
64
140
  hour,
65
141
  minute,
142
+ day,
143
+ month,
66
144
  weekday,
67
145
  key: `${fields['year']}-${fields['month']}-${fields['day']}T${fields['hour']}:${fields['minute']}`
68
146
  };
69
147
  }
148
+ function matches(schedule, local) {
149
+ if (!schedule.minute.has(local.minute) ||
150
+ !schedule.hour.has(local.hour) ||
151
+ !schedule.month.has(local.month))
152
+ return false;
153
+ const dayOfMonthMatches = schedule.dayOfMonth.has(local.day);
154
+ const dayOfWeekMatches = schedule.dayOfWeek.has(local.weekday);
155
+ const dayMatches = schedule.dayOfMonthWildcard && schedule.dayOfWeekWildcard
156
+ ? true
157
+ : schedule.dayOfMonthWildcard
158
+ ? dayOfWeekMatches
159
+ : schedule.dayOfWeekWildcard
160
+ ? dayOfMonthMatches
161
+ : dayOfMonthMatches || dayOfWeekMatches;
162
+ return dayMatches;
163
+ }
70
164
  function localKey(formatter, time) {
71
165
  return localParts(formatter, time).key;
72
166
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.9.128",
3
+ "version": "0.9.130",
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.128",
34
- "@myagentroam/protocol": "0.9.128",
35
- "@myagentroam/code-navigation": "0.9.128"
33
+ "@myagentroam/protocol": "0.9.130",
34
+ "@myagentroam/code-navigation": "0.9.129",
35
+ "@myagentroam/agent": "0.9.130"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/ws": "^8.18.1"