@linxin666/dsh-client-ui-task-board 0.4.4 → 0.4.6
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/README.i18n.yaml +2 -2
- package/README.md +59 -20
- package/README.zh.md +59 -20
- package/lib/client.js +6208 -624
- package/lib/client.js.map +1 -1
- package/lib/index.js +4774 -493
- package/lib/types/client/PluginSettingsCard.d.ts +7 -0
- package/lib/types/client/TaskBoardSettingsCard.d.ts +31 -3
- package/lib/types/client/board/ClampedMarkdown.d.ts +3 -0
- package/lib/types/client/board/ConfirmDialog.d.ts +4 -1
- package/lib/types/client/board/EditTaskModal.d.ts +5 -2
- package/lib/types/client/board/LinkSubtaskModal.d.ts +5 -2
- package/lib/types/client/board/NewTaskModal.d.ts +4 -1
- package/lib/types/client/board/TagManagerModal.d.ts +26 -0
- package/lib/types/client/board/TaskBoard.d.ts +10 -1
- package/lib/types/client/board/TaskCard.d.ts +14 -1
- package/lib/types/client/board/TaskDetail.d.ts +3 -1
- package/lib/types/client/board/TaskForm.d.ts +4 -1
- package/lib/types/client/board/VerificationReport.d.ts +23 -0
- package/lib/types/client/board/card-view.d.ts +113 -0
- package/lib/types/client/board/icons.d.ts +32 -0
- package/lib/types/client/board/overlay.d.ts +69 -0
- package/lib/types/client/board/status-key.d.ts +15 -0
- package/lib/types/client/board/task-markdown.d.ts +19 -0
- package/lib/types/client/index.d.ts +23 -5
- package/lib/types/client/locales.d.ts +168 -1
- package/lib/types/client/native-panel.d.ts +6 -20
- package/lib/types/client/plugin-card-seat.d.ts +5 -0
- package/lib/types/client/schedule-zone.d.ts +50 -0
- package/lib/types/client/seats.d.ts +18 -0
- package/lib/types/client/service.d.ts +32 -0
- package/lib/types/client/settings-form.d.ts +16 -0
- package/lib/types/core/controller.d.ts +46 -11
- package/lib/types/core/extension.d.ts +283 -0
- package/lib/types/core/handover.d.ts +13 -1
- package/lib/types/core/issue-form.d.ts +16 -0
- package/lib/types/core/poll-cadence.d.ts +31 -0
- package/lib/types/core/tasks.d.ts +228 -17
- package/lib/types/core/use-cases/task-schedule.d.ts +68 -10
- package/lib/types/core/use-cases/task-tag.d.ts +48 -0
- package/lib/types/core/use-cases/task-update.d.ts +5 -3
- package/lib/types/core/verification-budget.d.ts +52 -0
- package/lib/types/core/verification.d.ts +753 -0
- package/lib/types/core/workspace-target.d.ts +41 -0
- package/lib/types/host/agent-tools.d.ts +1 -1
- package/lib/types/host/extension-registry.d.ts +111 -0
- package/lib/types/host/llm-dispatch.d.ts +63 -0
- package/lib/types/host/verification-gate.d.ts +95 -0
- package/lib/types/host/verification-runner.d.ts +139 -0
- package/lib/types/host/workspace-evidence.d.ts +53 -0
- package/lib/types/host-ledger.d.ts +133 -14
- package/lib/types/host-runner.d.ts +55 -10
- package/lib/types/host-service.d.ts +205 -11
- package/lib/types/index.d.ts +134 -26
- package/lib/types/protocol.d.ts +92 -10
- package/lib/types/tool-surface.d.ts +71 -0
- package/package.json +28 -31
- package/src/client/PluginSettingsCard.tsx +9 -0
- package/src/client/TaskBoardSettingsCard.tsx +378 -66
- package/src/client/board/ClampedMarkdown.tsx +69 -0
- package/src/client/board/ConfirmDialog.tsx +7 -3
- package/src/client/board/EditTaskModal.tsx +5 -2
- package/src/client/board/LinkSubtaskModal.tsx +12 -7
- package/src/client/board/NewTaskModal.tsx +168 -21
- package/src/client/board/TagManagerModal.tsx +235 -0
- package/src/client/board/TaskBoard.tsx +259 -103
- package/src/client/board/TaskCard.tsx +214 -81
- package/src/client/board/TaskDetail.tsx +289 -43
- package/src/client/board/TaskForm.tsx +13 -3
- package/src/client/board/VerificationReport.tsx +322 -0
- package/src/client/board/card-view.ts +220 -0
- package/src/client/board/icons.tsx +89 -0
- package/src/client/board/overlay.tsx +189 -0
- package/src/client/board/status-key.ts +30 -0
- package/src/client/board/task-markdown.module.css +137 -0
- package/src/client/board/task-markdown.tsx +106 -0
- package/src/client/board-settings.module.css +66 -0
- package/src/client/board.module.css +488 -63
- package/src/client/index.ts +82 -51
- package/src/client/locales.ts +343 -6
- package/src/client/native-panel.tsx +18 -2
- package/src/client/plugin-card-seat.ts +7 -0
- package/src/client/schedule-zone.ts +100 -2
- package/src/client/seats.tsx +58 -0
- package/src/client/service.ts +94 -0
- package/src/client/settings-card.module.css +31 -5
- package/src/client/settings-form.ts +27 -1
- package/src/core/controller.ts +105 -10
- package/src/core/extension.ts +376 -0
- package/src/core/freeze-snapshot.ts +2 -2
- package/src/core/handover.ts +16 -1
- package/src/core/issue-form.ts +19 -0
- package/src/core/poll-cadence.ts +41 -0
- package/src/core/store.ts +80 -15
- package/src/core/tasks.ts +293 -45
- package/src/core/use-cases/task-create.ts +30 -9
- package/src/core/use-cases/task-parent.ts +3 -2
- package/src/core/use-cases/task-schedule.ts +190 -25
- package/src/core/use-cases/task-tag.ts +110 -0
- package/src/core/use-cases/task-update.ts +13 -7
- package/src/core/verification-budget.ts +115 -0
- package/src/core/verification.ts +1594 -0
- package/src/core/workspace-target.ts +59 -0
- package/src/host/agent-tools.ts +125 -38
- package/src/host/extension-registry.ts +509 -0
- package/src/host/llm-dispatch.ts +87 -0
- package/src/host/verification-gate.ts +487 -0
- package/src/host/verification-runner.ts +702 -0
- package/src/host/workspace-evidence.ts +176 -0
- package/src/host-ai.ts +15 -9
- package/src/host-ledger.ts +468 -71
- package/src/host-routes.ts +15 -2
- package/src/host-runner.ts +106 -24
- package/src/host-service.ts +546 -40
- package/src/index.ts +305 -21
- package/src/protocol.ts +226 -28
- package/src/tool-surface.ts +95 -0
- package/lib/invariant.js +0 -6
- package/lib/types/invariant.d.ts +0 -3
- package/src/invariant.ts +0 -4
package/src/host-routes.ts
CHANGED
|
@@ -208,7 +208,7 @@ export function makeTaskBoardRoutes(
|
|
|
208
208
|
if (parsed.action.kind !== 'import' && Buffer.byteLength(body.raw) > ACTION_LIMIT) {
|
|
209
209
|
return writeJson(res, 413, { ok: false, error: 'body-too-large' }, { 'cache-control': 'no-store' })
|
|
210
210
|
}
|
|
211
|
-
writeJson(res, 200, service.apply(parsed.requestId, parsed.action, parsed.initiator), { 'cache-control': 'no-store' })
|
|
211
|
+
writeJson(res, 200, await service.apply(parsed.requestId, parsed.action, parsed.initiator), { 'cache-control': 'no-store' })
|
|
212
212
|
} catch (error) {
|
|
213
213
|
const message = error instanceof Error ? error.message : String(error)
|
|
214
214
|
writeJson(res, message === 'body-too-large' ? 413 : 400, { ok: false, error: message }, { 'cache-control': 'no-store' })
|
|
@@ -289,5 +289,18 @@ export function makeTaskBoardRoutes(
|
|
|
289
289
|
}
|
|
290
290
|
},
|
|
291
291
|
}
|
|
292
|
-
|
|
292
|
+
const verification: WebRoute = {
|
|
293
|
+
kind: 'exact',
|
|
294
|
+
path: `${TASK_BOARD_API_PREFIX}/verification`,
|
|
295
|
+
handler: async (req, res): Promise<void> => {
|
|
296
|
+
if (req.method !== 'GET') return writeJson(res, 405, { ok: false, error: 'method-not-allowed' }, { 'cache-control': 'no-store' })
|
|
297
|
+
if (!guard(req, res)) return
|
|
298
|
+
try {
|
|
299
|
+
writeJson(res, 200, await service.verificationOptions(), { 'cache-control': 'no-store' })
|
|
300
|
+
} catch (error) {
|
|
301
|
+
writeJson(res, 500, { ok: false, error: error instanceof Error ? error.message : String(error) }, { 'cache-control': 'no-store' })
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
}
|
|
305
|
+
return [state, action, events, parse, verification]
|
|
293
306
|
}
|
package/src/host-runner.ts
CHANGED
|
@@ -3,9 +3,13 @@ import type { SessionAddress, SessionHistoryRecord, SessionListValue, SessionPag
|
|
|
3
3
|
import type { CommandResult } from '@deepseek-ai/dsh-commands/types'
|
|
4
4
|
import type { Workspace } from '@deepseek-ai/dsh-workspace/types'
|
|
5
5
|
import { teammateName } from './core/subtask.ts'
|
|
6
|
+
import { mostRecentWorkspaceId } from './core/workspace-target.ts'
|
|
6
7
|
import type { TaskPermission, TaskRecord } from './core/tasks.ts'
|
|
7
8
|
|
|
8
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Host services needed to validate a task's workspace before creating a
|
|
11
|
+
* session, and to resolve the workspace an unpinned card runs in.
|
|
12
|
+
*/
|
|
9
13
|
export interface TaskBoardWorkspaceRegistry {
|
|
10
14
|
list(): readonly Workspace[]
|
|
11
15
|
}
|
|
@@ -155,12 +159,12 @@ export interface PromptContext {
|
|
|
155
159
|
/** True when those members run as teammates inside this session (Team Lead). */
|
|
156
160
|
team?: boolean
|
|
157
161
|
/**
|
|
158
|
-
* Scheduled-run provenance: the instant the
|
|
159
|
-
* IANA zone its wall clock was read in. Present only for a
|
|
160
|
-
* run, so the model can resolve an unqualified date in the
|
|
161
|
-
* instead of guessing from the Host clock (issue #1722).
|
|
162
|
+
* Scheduled-run provenance: the instant the rule actually fired, its kind,
|
|
163
|
+
* and the IANA zone its wall clock was read in. Present only for a
|
|
164
|
+
* timer-triggered run, so the model can resolve an unqualified date in the
|
|
165
|
+
* run's own terms instead of guessing from the Host clock (issue #1722).
|
|
162
166
|
*/
|
|
163
|
-
schedule?: { triggeredAt: number; timeZone: string;
|
|
167
|
+
schedule?: { triggeredAt: number; timeZone: string; mode: 'cron' | 'once'; cron?: string }
|
|
164
168
|
}
|
|
165
169
|
|
|
166
170
|
/**
|
|
@@ -180,16 +184,20 @@ function peerPromptPreamble(context: PromptContext): string | undefined {
|
|
|
180
184
|
}
|
|
181
185
|
|
|
182
186
|
/**
|
|
183
|
-
* The scheduled-run section: this execution was triggered by the card's
|
|
184
|
-
*
|
|
185
|
-
* and the rule text, so the run's own clock is unambiguous — the board's
|
|
187
|
+
* The scheduled-run section: this execution was triggered by the card's
|
|
188
|
+
* schedule rather than by a person. It states the firing instant, the rule's
|
|
189
|
+
* zone, and the rule text, so the run's own clock is unambiguous — the board's
|
|
186
190
|
* scheduler fires on a wall clock in a specific IANA zone, which is not
|
|
187
|
-
* necessarily the zone of whatever session the run lands in.
|
|
191
|
+
* necessarily the zone of whatever session the run lands in. A one-shot says
|
|
192
|
+
* so explicitly, so the run does not expect a recurrence.
|
|
188
193
|
*/
|
|
189
194
|
function schedulePromptPreamble(context: PromptContext): string | undefined {
|
|
190
195
|
const schedule = context.schedule
|
|
191
196
|
if (schedule === undefined) return undefined
|
|
192
197
|
const stamp = new Date(schedule.triggeredAt).toISOString()
|
|
198
|
+
if (schedule.mode === 'once') {
|
|
199
|
+
return `本次执行由任务看板的单次定时自动触发:计划时间 ${stamp}(UTC),时区 ${schedule.timeZone}。这是一次性计划,本次执行之后该卡片不会再自动执行。需要判断「今天」「现在」或计算时间窗口时,以该时区的触发时间为准。`
|
|
200
|
+
}
|
|
193
201
|
return `本次执行由任务看板的定时规则自动触发:触发时间 ${stamp}(UTC),规则时区 ${schedule.timeZone},cron 表达式 ${schedule.cron}。需要判断「今天」「现在」或计算时间窗口时,以该时区的触发时间为准。`
|
|
194
202
|
}
|
|
195
203
|
|
|
@@ -298,6 +306,8 @@ function nonCompletedTurnEnd(data: unknown): string | undefined {
|
|
|
298
306
|
*/
|
|
299
307
|
function invokeWireArgs(namespace: string, method: string, request: Record<string, unknown>): Record<string, unknown> {
|
|
300
308
|
if (namespace === 'agentPresets' && method === 'list') return {}
|
|
309
|
+
// session/modelCatalog declares zero parameters, so its args must be {}.
|
|
310
|
+
if (namespace === 'session' && method === 'modelCatalog') return {}
|
|
301
311
|
if (namespace === 'session' && method === 'list') return { _request: request }
|
|
302
312
|
return { request }
|
|
303
313
|
}
|
|
@@ -328,6 +338,18 @@ export class HostExecutionRunner {
|
|
|
328
338
|
return this.gateway.stream({ namespace, method, args: { request }, ...(signal === undefined ? {} : { signal }) })
|
|
329
339
|
}
|
|
330
340
|
|
|
341
|
+
/**
|
|
342
|
+
* The workspace an unpinned run lands in: the deployment's most recently used
|
|
343
|
+
* one. Undefined when the deployment serves no workspace registry or has
|
|
344
|
+
* registered none yet; the launch then keeps the "let the Host decide" shape,
|
|
345
|
+
* which is the only remaining path to the Host's own working directory.
|
|
346
|
+
* @returns the workspace id to create the session in, when one is known.
|
|
347
|
+
*/
|
|
348
|
+
private recentWorkspaceId(): string | undefined {
|
|
349
|
+
if (this.workspaceRegistry === undefined) return undefined
|
|
350
|
+
return mostRecentWorkspaceId(this.workspaceRegistry.list())
|
|
351
|
+
}
|
|
352
|
+
|
|
331
353
|
/**
|
|
332
354
|
* Launch one execution. Without `options.reuseSessionId` a fresh session is
|
|
333
355
|
* created, renamed, pinned, and prompted (the historical contract). With it,
|
|
@@ -338,10 +360,31 @@ export class HostExecutionRunner {
|
|
|
338
360
|
* @param options - optional session to continue in.
|
|
339
361
|
* @returns the session id the execution runs in.
|
|
340
362
|
*/
|
|
341
|
-
async launch(task: TaskRecord, options: {
|
|
363
|
+
async launch(task: TaskRecord, options: {
|
|
364
|
+
reuseSessionId?: string
|
|
365
|
+
promptContext?: PromptContext
|
|
366
|
+
onSession?: (sessionId: string) => void
|
|
367
|
+
onGoalArmed?: (armed: boolean) => void
|
|
368
|
+
/**
|
|
369
|
+
* GLOBAL native /goal switch, resolved by the caller ONCE per execution and
|
|
370
|
+
* carried here so the decision is frozen for this launch.
|
|
371
|
+
*
|
|
372
|
+
* Frozen deliberately: a settings edit made while the run is in flight must
|
|
373
|
+
* not arm, disarm or otherwise change the goal/acceptance contract of a run
|
|
374
|
+
* already running. Absent means off — the default for every deployment that
|
|
375
|
+
* never touched the switch, including cards written before the option
|
|
376
|
+
* existed. A plain single turn is what an off switch produces; the task-level
|
|
377
|
+
* `goalRun` still applies on top of it (both must ASK for a goal).
|
|
378
|
+
*/
|
|
379
|
+
goalEnabled?: boolean
|
|
380
|
+
} = {}): Promise<string> {
|
|
342
381
|
// A handover bundle overrides the legacy pin fields: the bundle is the
|
|
343
|
-
// authoritative execution triplet for a continuation card (issue #5).
|
|
344
|
-
|
|
382
|
+
// authoritative execution triplet for a continuation card (issue #5). A
|
|
383
|
+
// card that pins nothing is resolved to the deployment's most recently used
|
|
384
|
+
// workspace instead of being left to `session.create`, whose own fallback is
|
|
385
|
+
// the Host process working directory (the desktop app's profile directory) —
|
|
386
|
+
// never the project the user was in when the card was written.
|
|
387
|
+
const workspaceId = task.handover?.workspaceId ?? task.workspaceId ?? this.recentWorkspaceId()
|
|
345
388
|
const mode = task.handover?.mode ?? task.mode
|
|
346
389
|
const permission = task.handover?.permission ?? task.permission
|
|
347
390
|
|
|
@@ -369,8 +412,12 @@ export class HostExecutionRunner {
|
|
|
369
412
|
// whose session was composed from a different preset now fails closed
|
|
370
413
|
// instead of silently running under the wrong composition, matching
|
|
371
414
|
// the fresh branch's `agentPreset` assertion (issue #1708).
|
|
415
|
+
// Report the session before anything is queued into it, so the board
|
|
416
|
+
// can bind the execution first and a completion claim can never arrive
|
|
417
|
+
// at the gate without its binding.
|
|
418
|
+
options.onSession?.(reused)
|
|
372
419
|
await this.assertReusedPreset(reused, mode)
|
|
373
|
-
await this.pinAndPrompt(reused, task, permission, options.promptContext)
|
|
420
|
+
await this.pinAndPrompt(reused, task, permission, options.promptContext, options.onGoalArmed, options.goalEnabled === true)
|
|
374
421
|
} catch (error) {
|
|
375
422
|
throw new SessionLaunchError(reused, error)
|
|
376
423
|
}
|
|
@@ -381,9 +428,10 @@ export class HostExecutionRunner {
|
|
|
381
428
|
...(mode === undefined ? {} : { agentPreset: mode }),
|
|
382
429
|
}) as { sessionId: ExecutionSessionId }
|
|
383
430
|
const sessionId = created.sessionId
|
|
431
|
+
options.onSession?.(sessionId)
|
|
384
432
|
try {
|
|
385
433
|
await this.invoke('session', 'rename', { sessionId, title: task.title })
|
|
386
|
-
await this.pinAndPrompt(sessionId, task, permission, options.promptContext)
|
|
434
|
+
await this.pinAndPrompt(sessionId, task, permission, options.promptContext, options.onGoalArmed, options.goalEnabled === true)
|
|
387
435
|
} catch (error) {
|
|
388
436
|
throw new SessionLaunchError(sessionId, error)
|
|
389
437
|
}
|
|
@@ -428,6 +476,8 @@ export class HostExecutionRunner {
|
|
|
428
476
|
task: TaskRecord,
|
|
429
477
|
permission: TaskPermission | undefined,
|
|
430
478
|
context: PromptContext = {},
|
|
479
|
+
onGoalArmed?: (armed: boolean) => void,
|
|
480
|
+
goalEnabled = false,
|
|
431
481
|
): Promise<void> {
|
|
432
482
|
if (permission !== undefined) {
|
|
433
483
|
if (this.commands === undefined) throw new Error('permission command dispatcher is unavailable')
|
|
@@ -456,7 +506,25 @@ export class HostExecutionRunner {
|
|
|
456
506
|
mode: 'queue' as const,
|
|
457
507
|
content: [{ type: 'text' as const, text: promptText(task, context) }],
|
|
458
508
|
})
|
|
459
|
-
await this.armGoal(sessionId, task, context)
|
|
509
|
+
const armed = await this.armGoal(sessionId, task, context, goalEnabled)
|
|
510
|
+
onGoalArmed?.(armed)
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Read the host's model catalog: the default route an unconfigured session
|
|
515
|
+
* starts at, and each provider's models with the reasoning metadata its
|
|
516
|
+
* adapter exposes. The acceptance settings card and the frozen contract both
|
|
517
|
+
* resolve against it, so "inherit host" means the host's own configuration
|
|
518
|
+
* rather than a session's or a card's pinned model.
|
|
519
|
+
* @returns the raw catalog value, or undefined when this cohort serves none.
|
|
520
|
+
*/
|
|
521
|
+
async modelCatalog(): Promise<unknown> {
|
|
522
|
+
try {
|
|
523
|
+
return await this.invoke('session', 'modelCatalog', {})
|
|
524
|
+
} catch (error) {
|
|
525
|
+
console.warn('[dsh-task-board] session/modelCatalog failed; the acceptance settings fall back to the configured route', error)
|
|
526
|
+
return undefined
|
|
527
|
+
}
|
|
460
528
|
}
|
|
461
529
|
|
|
462
530
|
/**
|
|
@@ -466,17 +534,28 @@ export class HostExecutionRunner {
|
|
|
466
534
|
* dsh's goal-round driver keeps starting continuation rounds until the agent
|
|
467
535
|
* marks it complete (see {@link goalVerdict} for how the run settles).
|
|
468
536
|
*
|
|
469
|
-
*
|
|
537
|
+
* Two switches gate the arming, and BOTH must ask for it:
|
|
538
|
+
* - the GLOBAL native-/goal switch (`goalEnabled`, default OFF), frozen when
|
|
539
|
+
* this execution started; and
|
|
540
|
+
* - the task's own `goalRun` (absent means off, only an explicit true opts
|
|
541
|
+
* the card into a goal run).
|
|
542
|
+
*
|
|
470
543
|
* A refusal (no command dispatcher, no `/goal` command in this cohort, an
|
|
471
|
-
* objective the command rejects) is reported and the run continues as
|
|
472
|
-
*
|
|
473
|
-
*
|
|
544
|
+
* objective the command rejects) is reported and the run continues as a plain
|
|
545
|
+
* turn: the task was asked to run, and a missing goal mode must not lose the
|
|
546
|
+
* work.
|
|
547
|
+
* @param sessionId - the session to arm in.
|
|
548
|
+
* @param task - the task being run.
|
|
549
|
+
* @param context - the run's prompt context.
|
|
550
|
+
* @param goalEnabled - the GLOBAL switch, frozen for this execution.
|
|
551
|
+
* @returns whether `/goal` was really armed.
|
|
474
552
|
*/
|
|
475
|
-
private async armGoal(sessionId: ExecutionSessionId, task: TaskRecord, context: PromptContext): Promise<
|
|
476
|
-
if (
|
|
553
|
+
private async armGoal(sessionId: ExecutionSessionId, task: TaskRecord, context: PromptContext, goalEnabled: boolean): Promise<boolean> {
|
|
554
|
+
if (!goalEnabled) return false
|
|
555
|
+
if (task.goalRun !== true) return false
|
|
477
556
|
if (this.commands === undefined) {
|
|
478
557
|
console.warn('[dsh-task-board] no command dispatcher is available; task ' + task.id + ' runs without /goal')
|
|
479
|
-
return
|
|
558
|
+
return false
|
|
480
559
|
}
|
|
481
560
|
try {
|
|
482
561
|
const command = await this.commands.execute(
|
|
@@ -486,13 +565,16 @@ export class HostExecutionRunner {
|
|
|
486
565
|
)
|
|
487
566
|
if (command === undefined) {
|
|
488
567
|
console.warn('[dsh-task-board] /goal was not acknowledged for session ' + sessionId + '; the run continues as a plain turn')
|
|
489
|
-
return
|
|
568
|
+
return false
|
|
490
569
|
}
|
|
491
570
|
if (command.kind !== 'success') {
|
|
492
571
|
console.warn('[dsh-task-board] /goal was refused for session ' + sessionId + ': ' + (command.text ?? 'no reason reported') + '; the run continues as a plain turn')
|
|
572
|
+
return false
|
|
493
573
|
}
|
|
574
|
+
return true
|
|
494
575
|
} catch (error) {
|
|
495
576
|
console.warn('[dsh-task-board] could not arm /goal on session ' + sessionId + '; the run continues as a plain turn', error)
|
|
577
|
+
return false
|
|
496
578
|
}
|
|
497
579
|
}
|
|
498
580
|
|