@things-factory/ai-assistant 10.1.203 → 10.1.206
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-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/assistant-mode/index.d.ts +1 -1
- package/dist-server/service/assistant-mode/index.js.map +1 -1
- package/dist-server/service/assistant-mode/mode-categories.d.ts +10 -0
- package/dist-server/service/assistant-mode/mode-categories.js +10 -0
- package/dist-server/service/assistant-mode/mode-categories.js.map +1 -1
- package/dist-server/service/assistant-mode/types.d.ts +14 -0
- package/dist-server/service/assistant-mode/types.js.map +1 -1
- package/dist-server/service/index.d.ts +2 -1
- package/dist-server/service/index.js +4 -1
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/unattended-run/assistant-run.d.ts +45 -0
- package/dist-server/service/unattended-run/assistant-run.js +248 -0
- package/dist-server/service/unattended-run/assistant-run.js.map +1 -0
- package/dist-server/service/unattended-run/index.d.ts +8 -0
- package/dist-server/service/unattended-run/index.js +15 -0
- package/dist-server/service/unattended-run/index.js.map +1 -0
- package/dist-server/service/unattended-run/limits.d.ts +9 -0
- package/dist-server/service/unattended-run/limits.js +24 -0
- package/dist-server/service/unattended-run/limits.js.map +1 -0
- package/dist-server/service/unattended-run/run-as.d.ts +24 -0
- package/dist-server/service/unattended-run/run-as.js +26 -0
- package/dist-server/service/unattended-run/run-as.js.map +1 -0
- package/dist-server/service/unattended-run/run-budget.d.ts +24 -0
- package/dist-server/service/unattended-run/run-budget.js +46 -0
- package/dist-server/service/unattended-run/run-budget.js.map +1 -0
- package/dist-server/service/unattended-run/run-ledger.d.ts +13 -0
- package/dist-server/service/unattended-run/run-ledger.js +29 -0
- package/dist-server/service/unattended-run/run-ledger.js.map +1 -0
- package/dist-server/service/unattended-run/run-outcome.d.ts +9 -0
- package/dist-server/service/unattended-run/run-outcome.js +46 -0
- package/dist-server/service/unattended-run/run-outcome.js.map +1 -0
- package/dist-server/service/unattended-run/run-store.d.ts +31 -0
- package/dist-server/service/unattended-run/run-store.js +108 -0
- package/dist-server/service/unattended-run/run-store.js.map +1 -0
- package/dist-server/service/unattended-run/run-unattended.d.ts +50 -0
- package/dist-server/service/unattended-run/run-unattended.js +214 -0
- package/dist-server/service/unattended-run/run-unattended.js.map +1 -0
- package/dist-server/service/unattended-run/unattended-prompt.d.ts +3 -0
- package/dist-server/service/unattended-run/unattended-prompt.js +27 -0
- package/dist-server/service/unattended-run/unattended-prompt.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/server/service/assistant-mode/index.ts +1 -1
- package/server/service/assistant-mode/mode-categories.ts +10 -0
- package/server/service/assistant-mode/types.ts +15 -0
- package/server/service/index.ts +4 -1
- package/server/service/unattended-run/assistant-run.ts +199 -0
- package/server/service/unattended-run/index.ts +10 -0
- package/server/service/unattended-run/limits.ts +24 -0
- package/server/service/unattended-run/run-as.ts +38 -0
- package/server/service/unattended-run/run-budget.ts +58 -0
- package/server/service/unattended-run/run-ledger.ts +31 -0
- package/server/service/unattended-run/run-outcome.ts +43 -0
- package/server/service/unattended-run/run-store-race.pg.test.ts +83 -0
- package/server/service/unattended-run/run-store.ts +162 -0
- package/server/service/unattended-run/run-unattended.test.ts +477 -0
- package/server/service/unattended-run/run-unattended.ts +246 -0
- package/server/service/unattended-run/unattended-prompt.ts +24 -0
- package/translations/en.json +15 -1
- package/translations/es.json +15 -1
- package/translations/ja.json +15 -1
- package/translations/ko.json +15 -1
- package/translations/ms.json +15 -1
- package/translations/zh.json +15 -1
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Run an assistant mode with no person in it, for scheduled work, and keep what it wrote as a draft
|
|
3
|
+
* (operato-application/design/ai/unattended-run.md, ADR-0142 §8.1, user ruling C 2026-10-20).
|
|
4
|
+
*
|
|
5
|
+
* A server function and no GraphQL door: a door would let a person name someone else as `runAs`. The caller is a
|
|
6
|
+
* server's scheduled task - Office's periodic strategy review is the first.
|
|
7
|
+
*
|
|
8
|
+
* identity (domain, source, subject, scheduledAt) is one row - a retry hands the stored run back (§5)
|
|
9
|
+
* who the run-as user, judged again now; refused when they cannot ask the assistant themselves (§2)
|
|
10
|
+
* tools the mode's read tools behind that user's own doors; no 입력안, no cards kept (§3)
|
|
11
|
+
* limits the mode's: model requests, tokens, wall time (§4)
|
|
12
|
+
* outcome succeeded with a draft, failed or refused with a code - never an empty success (§7)
|
|
13
|
+
*
|
|
14
|
+
* Every tool call goes through the registry door, so the recorders write it down as they do a turn's (§6).
|
|
15
|
+
*/
|
|
16
|
+
import { aiClientFor, findToolSpec, registryAssistantChat, type AgentClient, type AiClientRequest, type ToolAccessCheck, type ToolCallRecord } from '@things-factory/ai-client-base'
|
|
17
|
+
import { User, canUserCheckinDomain, checkPermission } from '@things-factory/auth-base'
|
|
18
|
+
import { getRepository } from '@things-factory/shell'
|
|
19
|
+
|
|
20
|
+
import { getAssistantMode } from '../assistant-mode/registry.js'
|
|
21
|
+
import { unattendedToolCategories } from '../assistant-mode/mode-categories.js'
|
|
22
|
+
import { doorsForAnswer } from '../chat-message/message-doors.js'
|
|
23
|
+
import { groundingSourcesFor } from '../grounding-sources.js'
|
|
24
|
+
import { toolAccessFor } from '../tool-access.js'
|
|
25
|
+
import { AssistantRun } from './assistant-run.js'
|
|
26
|
+
import { ABANDON_MARGIN_MS, unattendedLimits } from './limits.js'
|
|
27
|
+
import { budgetedClient, RunBudgetExceeded } from './run-budget.js'
|
|
28
|
+
import { judgeRunAs, type RunAsChecks } from './run-as.js'
|
|
29
|
+
import { typeormRunStore, type RunEnding, type RunStore } from './run-store.js'
|
|
30
|
+
import { unattendedPrompt } from './unattended-prompt.js'
|
|
31
|
+
|
|
32
|
+
export interface UnattendedRunInput {
|
|
33
|
+
/** The domain the run is in, as loaded - its parent and owner decide who can enter it. */
|
|
34
|
+
domain: { id: string; [key: string]: any }
|
|
35
|
+
/** The user the run acts as - the policy owner. Their privileges and doors only. */
|
|
36
|
+
runAs: string
|
|
37
|
+
/** Which occurrence this is. The same three again is the same run. */
|
|
38
|
+
run: {
|
|
39
|
+
/** Who scheduled it - `<app>.<work>`, e.g. `office.strategy-review`. */
|
|
40
|
+
source: string
|
|
41
|
+
/** What it is about in the source's terms - a policy id. */
|
|
42
|
+
subject: string
|
|
43
|
+
scheduledAt: Date
|
|
44
|
+
}
|
|
45
|
+
/** A registered mode that declares `unattended`. */
|
|
46
|
+
mode: { kind: string; target?: string }
|
|
47
|
+
/** What this occurrence is asked to do - the caller server's text, read by the model (English). */
|
|
48
|
+
instruction: string
|
|
49
|
+
/** What the run is about, for the tools. Filled by the caller's server; still never used for authorization. */
|
|
50
|
+
hostContext?: Record<string, any>
|
|
51
|
+
/** The language of the draft - the owner's. Absent: their account's locale. */
|
|
52
|
+
lng?: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** What the run reaches outside itself. The defaults are the server's; the tests hand their own. */
|
|
56
|
+
export interface UnattendedRunDeps {
|
|
57
|
+
store: RunStore
|
|
58
|
+
runAs: RunAsChecks<any, any>
|
|
59
|
+
clientFor: (request: AiClientRequest) => Promise<AgentClient>
|
|
60
|
+
toolAccess: (state: any) => ToolAccessCheck
|
|
61
|
+
now: () => Date
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const KEY = /^[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*$/
|
|
65
|
+
|
|
66
|
+
function invalid(field: string): Error {
|
|
67
|
+
const error: any = new Error(`runAssistantUnattended: '${field}' is missing or not usable`)
|
|
68
|
+
error.code = 'assistant-run-input-invalid'
|
|
69
|
+
error.params = { field }
|
|
70
|
+
return error
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function checkInput(input: UnattendedRunInput) {
|
|
74
|
+
if (!input?.domain?.id) throw invalid('domain')
|
|
75
|
+
if (!input.runAs) throw invalid('runAs')
|
|
76
|
+
if (!KEY.test(String(input.run?.source ?? '')) || input.run.source.length > 64) throw invalid('run.source')
|
|
77
|
+
if (!String(input.run.subject ?? '').trim() || String(input.run.subject).length > 64) throw invalid('run.subject')
|
|
78
|
+
if (!(input.run.scheduledAt instanceof Date) || !Number.isFinite(input.run.scheduledAt.valueOf())) throw invalid('run.scheduledAt')
|
|
79
|
+
if (!input.mode?.kind) throw invalid('mode.kind')
|
|
80
|
+
if (!String(input.instruction ?? '').trim()) throw invalid('instruction')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function defaultDeps(): UnattendedRunDeps {
|
|
84
|
+
return {
|
|
85
|
+
store: typeormRunStore(() => getRepository(AssistantRun)),
|
|
86
|
+
runAs: {
|
|
87
|
+
findUser: id => getRepository(User).findOne({ where: { id } }),
|
|
88
|
+
mayCheckin: (user, domain) => canUserCheckinDomain(user, domain),
|
|
89
|
+
/* No request: no address to refuse from, and no license prohibition - those cover writes this run never makes. */
|
|
90
|
+
permits: (gate, user, domain) => checkPermission(gate as any, user, domain, false, [])
|
|
91
|
+
},
|
|
92
|
+
clientFor: aiClientFor,
|
|
93
|
+
toolAccess: state => toolAccessFor({ state }),
|
|
94
|
+
now: () => new Date()
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Why the loop stopped, as the run's code. Only a clean end with text is a draft. */
|
|
99
|
+
function failureOf(loop: any, baseText: string): { code: string; params?: any } | undefined {
|
|
100
|
+
const abort = loop?.abortReason
|
|
101
|
+
if (abort?.type === 'max_iterations') return { code: 'assistant-run-step-limit', params: { steps: abort.iter } }
|
|
102
|
+
if (abort?.type === 'provider_error') return { code: 'assistant-run-model-error', params: { message: String(abort.message ?? '').slice(0, 500) } }
|
|
103
|
+
if (abort) return { code: 'assistant-run-model-error', params: { stopped: abort.type } }
|
|
104
|
+
if (!baseText.trim()) return { code: 'assistant-run-empty-draft' }
|
|
105
|
+
return undefined
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const TIMED_OUT = Symbol('timed-out')
|
|
109
|
+
|
|
110
|
+
export async function runAssistantUnattended(input: UnattendedRunInput, deps: UnattendedRunDeps = defaultDeps()): Promise<AssistantRun> {
|
|
111
|
+
checkInput(input)
|
|
112
|
+
const { domain, run } = input
|
|
113
|
+
const mode = getAssistantMode(input.mode.kind)
|
|
114
|
+
const limits = unattendedLimits(mode?.unattended)
|
|
115
|
+
const startedAt = deps.now()
|
|
116
|
+
/* The account as stored now - read once, and judged after the occurrence is held. */
|
|
117
|
+
const account = await deps.runAs.findUser(input.runAs)
|
|
118
|
+
|
|
119
|
+
const { claimed, row } = await deps.store.claim(
|
|
120
|
+
{ domainId: domain.id, source: run.source, subject: run.subject, scheduledAt: run.scheduledAt },
|
|
121
|
+
{
|
|
122
|
+
name: `${run.source} ${run.subject} ${run.scheduledAt.toISOString()}`,
|
|
123
|
+
modeKind: input.mode.kind,
|
|
124
|
+
modeTarget: input.mode.target,
|
|
125
|
+
runAsId: account?.id,
|
|
126
|
+
startedAt,
|
|
127
|
+
deadlineAt: new Date(startedAt.valueOf() + limits.timeoutMs + ABANDON_MARGIN_MS)
|
|
128
|
+
},
|
|
129
|
+
startedAt
|
|
130
|
+
)
|
|
131
|
+
/* Succeeded, or running elsewhere and still alive - the caller gets that row and the model is not called again. */
|
|
132
|
+
if (!claimed) return row
|
|
133
|
+
|
|
134
|
+
const held = { id: row.id!, attempt: row.attempt }
|
|
135
|
+
const end = (ending: RunEnding) => deps.store.finish(held, { finishedAt: deps.now(), ...ending })
|
|
136
|
+
const refuse = (code: string, params?: any) => end({ status: 'refused', outcomeCode: code, outcomeParams: params ? JSON.stringify(params) : null } as any)
|
|
137
|
+
const fail = (code: string, params?: any, spent?: RunEnding) =>
|
|
138
|
+
end({ status: 'failed', outcomeCode: code, outcomeParams: params ? JSON.stringify(params) : null, ...spent } as any)
|
|
139
|
+
|
|
140
|
+
if (!mode) return refuse('unknown-assistant-mode', { kind: input.mode.kind })
|
|
141
|
+
if (!mode.unattended) return refuse('assistant-mode-not-unattended', { kind: mode.kind })
|
|
142
|
+
|
|
143
|
+
const judged = await judgeRunAs(account, domain, deps.runAs)
|
|
144
|
+
if ('refused' in judged) return refuse(judged.refused, { userId: input.runAs })
|
|
145
|
+
const user = judged.user
|
|
146
|
+
|
|
147
|
+
let agentClient: AgentClient
|
|
148
|
+
try {
|
|
149
|
+
agentClient = await deps.clientFor({ domain: domain as any, user, modeKind: mode.kind })
|
|
150
|
+
} catch (error: any) {
|
|
151
|
+
/* No AI here, or not the agent asked for - nothing ran. Its own code, as the conversation door says it. */
|
|
152
|
+
if (error?.code) return refuse(String(error.code), error.params)
|
|
153
|
+
return fail('assistant-run-model-error', { message: String(error?.message ?? error).slice(0, 500) })
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const { client, spend } = budgetedClient(agentClient, limits)
|
|
157
|
+
const reads: ToolCallRecord[] = []
|
|
158
|
+
let over = false
|
|
159
|
+
const state: any = { domain, user, lng: input.lng ?? user.locale, aiAgentId: agentClient.agent.id }
|
|
160
|
+
const categories = unattendedToolCategories(mode)
|
|
161
|
+
const systemPrompt = unattendedPrompt(mode.systemPrompt, state.lng)
|
|
162
|
+
const messages = [{ role: 'user' as const, content: input.instruction }]
|
|
163
|
+
|
|
164
|
+
const abort = new AbortController()
|
|
165
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
166
|
+
const timeout = new Promise<typeof TIMED_OUT>(resolve => {
|
|
167
|
+
timer = setTimeout(() => {
|
|
168
|
+
abort.abort()
|
|
169
|
+
resolve(TIMED_OUT)
|
|
170
|
+
}, limits.timeoutMs)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
const spent = (): RunEnding => ({
|
|
174
|
+
reads: JSON.stringify(reads),
|
|
175
|
+
agentId: agentClient.agent.id,
|
|
176
|
+
clientId: agentClient.id,
|
|
177
|
+
modelCalls: spend.modelCalls,
|
|
178
|
+
inputTokens: spend.inputTokens,
|
|
179
|
+
outputTokens: spend.outputTokens
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
let result: any
|
|
183
|
+
try {
|
|
184
|
+
result = await Promise.race([
|
|
185
|
+
registryAssistantChat({
|
|
186
|
+
client,
|
|
187
|
+
messages,
|
|
188
|
+
systemPrompt,
|
|
189
|
+
toolCategories: categories,
|
|
190
|
+
mode: { kind: mode.kind, ...(input.mode.target ? { target: input.mode.target } : {}) },
|
|
191
|
+
toolAccess: deps.toolAccess(state),
|
|
192
|
+
hostContext: input.hostContext,
|
|
193
|
+
state,
|
|
194
|
+
temperature: mode.temperature,
|
|
195
|
+
maxTokens: mode.maxTokens,
|
|
196
|
+
requireToolOnFirstTurn: mode.requireToolOnFirstTurn,
|
|
197
|
+
/* Each step is one request. A run that reaches the last step without an answer failed - no wrap-up call (§4). */
|
|
198
|
+
maxIterations: limits.maxModelCalls,
|
|
199
|
+
salvageOnMaxIterations: false,
|
|
200
|
+
signal: abort.signal,
|
|
201
|
+
onToolCall: record => {
|
|
202
|
+
if (!over) reads.push(record)
|
|
203
|
+
},
|
|
204
|
+
run: { id: held.id, source: run.source, subject: run.subject, scheduledAt: run.scheduledAt.toISOString() },
|
|
205
|
+
groundingSources: groundingSourcesFor({ systemPrompt, messages: messages as any, hostContext: input.hostContext })
|
|
206
|
+
}),
|
|
207
|
+
timeout
|
|
208
|
+
])
|
|
209
|
+
} catch (error: any) {
|
|
210
|
+
over = true
|
|
211
|
+
clearTimeout(timer)
|
|
212
|
+
if (error instanceof RunBudgetExceeded) return fail(error.code, error.params, spent())
|
|
213
|
+
return fail('assistant-run-model-error', { message: String(error?.message ?? error).slice(0, 500) }, spent())
|
|
214
|
+
}
|
|
215
|
+
over = true
|
|
216
|
+
clearTimeout(timer)
|
|
217
|
+
|
|
218
|
+
if (result === TIMED_OUT) return fail('assistant-run-timeout', { timeoutMs: limits.timeoutMs }, spent())
|
|
219
|
+
/* The loop turns a refused request into a provider error; the budget says which limit it was. */
|
|
220
|
+
if (spend.exceeded) return fail('assistant-run-budget-exceeded', spend.exceeded, spent())
|
|
221
|
+
|
|
222
|
+
const failure = failureOf(result.loop, result.baseText ?? '')
|
|
223
|
+
if (failure) return fail(failure.code, failure.params, spent())
|
|
224
|
+
|
|
225
|
+
const doors = doorsForAnswer(
|
|
226
|
+
(result.loop.toolUsages ?? []).map((usage: any) => usage?.name).filter(Boolean),
|
|
227
|
+
name => (findToolSpec(name, categories) as any)?.doors
|
|
228
|
+
)
|
|
229
|
+
return end({
|
|
230
|
+
status: 'succeeded',
|
|
231
|
+
draft: result.baseText,
|
|
232
|
+
doors,
|
|
233
|
+
cardsDropped: result.loop.proposals?.length ?? 0,
|
|
234
|
+
groundingWarnings: result.groundingWarnings?.length ? JSON.stringify(result.groundingWarnings).slice(0, 1024) : null,
|
|
235
|
+
...spent()
|
|
236
|
+
} as any)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** The stored run of an occurrence, for the application that scheduled it - read behind that application's own door. */
|
|
240
|
+
export async function findAssistantRun(identity: { domainId: string; source: string; subject: string; scheduledAt: Date } | { id: string }): Promise<AssistantRun | null> {
|
|
241
|
+
const repository = getRepository(AssistantRun)
|
|
242
|
+
if ('id' in identity) return repository.findOne({ where: { id: identity.id } })
|
|
243
|
+
return repository.findOne({
|
|
244
|
+
where: { domain: { id: identity.domainId }, source: identity.source, subject: identity.subject, scheduledAt: identity.scheduledAt } as any
|
|
245
|
+
})
|
|
246
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The rules every unattended run follows, after the mode's persona (design/ai/unattended-run.md §3). The reader is the
|
|
3
|
+
* model, so the text is English (ADR-0061 A11); the draft is written in the owner's language.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const LANGUAGES: Record<string, string> = { ko: 'Korean', en: 'English', ja: 'Japanese', zh: 'Chinese', ms: 'Malay', vi: 'Vietnamese' }
|
|
7
|
+
|
|
8
|
+
export function languageName(lng: string | undefined): string {
|
|
9
|
+
const code = String(lng ?? '').trim().toLowerCase()
|
|
10
|
+
if (!code) return 'English'
|
|
11
|
+
return LANGUAGES[code] ?? LANGUAGES[code.split(/[-_]/)[0]] ?? `the language of locale "${code}"`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const UNATTENDED_RULES = [
|
|
15
|
+
'No person is reading this as it is written. It runs on a schedule, and your answer is kept as a draft that its owner reads later.',
|
|
16
|
+
'- Read before you write. Every figure and fact in the draft comes from a tool result of this run.',
|
|
17
|
+
'- A read that failed or was refused is said as failed, with what the draft cannot say because of it. Never write it as "no change".',
|
|
18
|
+
'- Nothing you call is shown to anyone: there are no cards and no screens to open. Put what matters in the text.',
|
|
19
|
+
'- Do not ask questions - nobody will answer. Where something is missing, say what is missing.'
|
|
20
|
+
].join('\n')
|
|
21
|
+
|
|
22
|
+
export function unattendedPrompt(persona: string, lng: string | undefined): string {
|
|
23
|
+
return `${persona}\n\n${UNATTENDED_RULES}\n- Write the draft in ${languageName(lng)}.`
|
|
24
|
+
}
|
package/translations/en.json
CHANGED
|
@@ -385,5 +385,19 @@
|
|
|
385
385
|
"ai-assistant.usage.total": "Total",
|
|
386
386
|
"ai-assistant.text.send-to-people-notice": "This goes to the people in this conversation. The AI does not answer it.",
|
|
387
387
|
"task.ai-assistant.use.name": "Working with the AI assistant",
|
|
388
|
-
"task.ai-assistant.use.description": "Talks with the assistant. It reads only through the caller's own privileges."
|
|
388
|
+
"task.ai-assistant.use.description": "Talks with the assistant. It reads only through the caller's own privileges.",
|
|
389
|
+
"ai-assistant.run.assistant-run-as-unknown": "the account the run was to act as does not exist.",
|
|
390
|
+
"ai-assistant.run.assistant-run-as-inactive": "the owner's account is not active, so the run did not start.",
|
|
391
|
+
"ai-assistant.run.assistant-run-as-not-member": "the owner can no longer enter this domain, so the run did not start.",
|
|
392
|
+
"ai-assistant.run.assistant-run-as-not-permitted": "the owner may no longer ask the AI assistant, so the run did not start.",
|
|
393
|
+
"ai-assistant.run.assistant-mode-not-unattended": "the assistant mode {kind} does not run without a person.",
|
|
394
|
+
"ai-assistant.run.unknown-assistant-mode": "this server has no assistant mode {kind}.",
|
|
395
|
+
"ai-assistant.run.assistant-run-budget-exceeded": "the run reached its {limit} limit ({used} of {max}) and stopped.",
|
|
396
|
+
"ai-assistant.run.assistant-run-step-limit": "the run did not finish within {steps} steps.",
|
|
397
|
+
"ai-assistant.run.assistant-run-timeout": "the run did not finish within {timeoutMs} ms.",
|
|
398
|
+
"ai-assistant.run.assistant-run-empty-draft": "the run ended without writing a draft.",
|
|
399
|
+
"ai-assistant.run.assistant-run-model-error": "the AI model failed during the run.",
|
|
400
|
+
"ai-assistant.run.assistant-run-abandoned": "the run stopped partway and was started again.",
|
|
401
|
+
"ai-assistant.run.ai-not-configured": "AI is not set up for this domain.",
|
|
402
|
+
"ai-assistant.run.ai-agent-not-available": "the AI agent picked for this run is not available here."
|
|
389
403
|
}
|
package/translations/es.json
CHANGED
|
@@ -385,5 +385,19 @@
|
|
|
385
385
|
"ai-assistant.usage.total": "Total",
|
|
386
386
|
"ai-assistant.text.send-to-people-notice": "Esto va a los participantes de esta conversación. La IA no responde.",
|
|
387
387
|
"task.ai-assistant.use.name": "Trabajar con el asistente de IA",
|
|
388
|
-
"task.ai-assistant.use.description": "Conversa con el asistente. Solo lee con los privilegios de quien lo llama."
|
|
388
|
+
"task.ai-assistant.use.description": "Conversa con el asistente. Solo lee con los privilegios de quien lo llama.",
|
|
389
|
+
"ai-assistant.run.assistant-run-as-unknown": "la cuenta con la que debía ejecutarse no existe.",
|
|
390
|
+
"ai-assistant.run.assistant-run-as-inactive": "la cuenta del propietario no está activa, así que no se ejecutó.",
|
|
391
|
+
"ai-assistant.run.assistant-run-as-not-member": "el propietario ya no puede entrar en este dominio, así que no se ejecutó.",
|
|
392
|
+
"ai-assistant.run.assistant-run-as-not-permitted": "el propietario ya no puede usar el asistente de IA, así que no se ejecutó.",
|
|
393
|
+
"ai-assistant.run.assistant-mode-not-unattended": "el modo de asistente {kind} no se ejecuta sin una persona.",
|
|
394
|
+
"ai-assistant.run.unknown-assistant-mode": "este servidor no tiene el modo de asistente {kind}.",
|
|
395
|
+
"ai-assistant.run.assistant-run-budget-exceeded": "la ejecución alcanzó su límite de {limit} ({used} de {max}) y se detuvo.",
|
|
396
|
+
"ai-assistant.run.assistant-run-step-limit": "la ejecución no terminó en {steps} pasos.",
|
|
397
|
+
"ai-assistant.run.assistant-run-timeout": "la ejecución no terminó en {timeoutMs} ms.",
|
|
398
|
+
"ai-assistant.run.assistant-run-empty-draft": "la ejecución terminó sin escribir un borrador.",
|
|
399
|
+
"ai-assistant.run.assistant-run-model-error": "el modelo de IA falló durante la ejecución.",
|
|
400
|
+
"ai-assistant.run.assistant-run-abandoned": "la ejecución se detuvo a medias y se inició de nuevo.",
|
|
401
|
+
"ai-assistant.run.ai-not-configured": "la IA no está configurada para este dominio.",
|
|
402
|
+
"ai-assistant.run.ai-agent-not-available": "el agente de IA elegido para esta ejecución no está disponible aquí."
|
|
389
403
|
}
|
package/translations/ja.json
CHANGED
|
@@ -385,5 +385,19 @@
|
|
|
385
385
|
"ai-assistant.usage.total": "合計",
|
|
386
386
|
"ai-assistant.text.send-to-people-notice": "この会話の参加者に送ります。AI は応答しません。",
|
|
387
387
|
"task.ai-assistant.use.name": "AIアシスタント",
|
|
388
|
-
"task.ai-assistant.use.description": "AIアシスタントと対話する。アシスタントは呼び出した人の権限でのみ読む。"
|
|
388
|
+
"task.ai-assistant.use.description": "AIアシスタントと対話する。アシスタントは呼び出した人の権限でのみ読む。",
|
|
389
|
+
"ai-assistant.run.assistant-run-as-unknown": "実行する利用者のアカウントがありません。",
|
|
390
|
+
"ai-assistant.run.assistant-run-as-inactive": "所有者のアカウントが有効でないため、実行しませんでした。",
|
|
391
|
+
"ai-assistant.run.assistant-run-as-not-member": "所有者がこの事業所に入れなくなったため、実行しませんでした。",
|
|
392
|
+
"ai-assistant.run.assistant-run-as-not-permitted": "所有者に AI アシスタントの利用権限がないため、実行しませんでした。",
|
|
393
|
+
"ai-assistant.run.assistant-mode-not-unattended": "アシスタントモード {kind} は人がいない状態では実行されません。",
|
|
394
|
+
"ai-assistant.run.unknown-assistant-mode": "このサーバーにはアシスタントモード {kind} がありません。",
|
|
395
|
+
"ai-assistant.run.assistant-run-budget-exceeded": "実行が {limit} の上限({max} 中 {used})に達して停止しました。",
|
|
396
|
+
"ai-assistant.run.assistant-run-step-limit": "実行が {steps} ステップ以内に終わりませんでした。",
|
|
397
|
+
"ai-assistant.run.assistant-run-timeout": "実行が {timeoutMs} ms 以内に終わりませんでした。",
|
|
398
|
+
"ai-assistant.run.assistant-run-empty-draft": "実行は下書きを書かずに終わりました。",
|
|
399
|
+
"ai-assistant.run.assistant-run-model-error": "実行中に AI モデルの呼び出しが失敗しました。",
|
|
400
|
+
"ai-assistant.run.assistant-run-abandoned": "実行が途中で止まったため、もう一度開始しました。",
|
|
401
|
+
"ai-assistant.run.ai-not-configured": "この事業所には AI が設定されていません。",
|
|
402
|
+
"ai-assistant.run.ai-agent-not-available": "この実行に選んだ AI エージェントはここでは使えません。"
|
|
389
403
|
}
|
package/translations/ko.json
CHANGED
|
@@ -385,5 +385,19 @@
|
|
|
385
385
|
"ai-assistant.usage.total": "합계",
|
|
386
386
|
"ai-assistant.text.send-to-people-notice": "이 대화의 참여자에게 보냅니다. AI 는 답하지 않습니다.",
|
|
387
387
|
"task.ai-assistant.use.name": "AI 도우미",
|
|
388
|
-
"task.ai-assistant.use.description": "AI 도우미와 대화한다. 도우미는 부른 사람의 권한으로만 읽는다."
|
|
388
|
+
"task.ai-assistant.use.description": "AI 도우미와 대화한다. 도우미는 부른 사람의 권한으로만 읽는다.",
|
|
389
|
+
"ai-assistant.run.assistant-run-as-unknown": "실행할 사용자 계정이 없습니다.",
|
|
390
|
+
"ai-assistant.run.assistant-run-as-inactive": "소유자 계정이 활성 상태가 아니어서 실행하지 않았습니다.",
|
|
391
|
+
"ai-assistant.run.assistant-run-as-not-member": "소유자가 이 사업장에 더 이상 들어올 수 없어 실행하지 않았습니다.",
|
|
392
|
+
"ai-assistant.run.assistant-run-as-not-permitted": "소유자에게 AI 어시스턴트 사용 권한이 없어 실행하지 않았습니다.",
|
|
393
|
+
"ai-assistant.run.assistant-mode-not-unattended": "어시스턴트 모드 {kind} 는 사람 없이 실행되지 않습니다.",
|
|
394
|
+
"ai-assistant.run.unknown-assistant-mode": "이 서버에 어시스턴트 모드 {kind} 가 없습니다.",
|
|
395
|
+
"ai-assistant.run.assistant-run-budget-exceeded": "실행이 {limit} 한도({max} 중 {used})에 닿아 멈췄습니다.",
|
|
396
|
+
"ai-assistant.run.assistant-run-step-limit": "실행이 {steps} 단계 안에 끝나지 않았습니다.",
|
|
397
|
+
"ai-assistant.run.assistant-run-timeout": "실행이 {timeoutMs} ms 안에 끝나지 않았습니다.",
|
|
398
|
+
"ai-assistant.run.assistant-run-empty-draft": "실행이 검토안을 쓰지 않고 끝났습니다.",
|
|
399
|
+
"ai-assistant.run.assistant-run-model-error": "실행 중 AI 모델 호출이 실패했습니다.",
|
|
400
|
+
"ai-assistant.run.assistant-run-abandoned": "실행이 도중에 멈춰 다시 시작했습니다.",
|
|
401
|
+
"ai-assistant.run.ai-not-configured": "이 사업장에는 AI 가 설정되어 있지 않습니다.",
|
|
402
|
+
"ai-assistant.run.ai-agent-not-available": "이 실행에 고른 AI 에이전트를 여기서 쓸 수 없습니다."
|
|
389
403
|
}
|
package/translations/ms.json
CHANGED
|
@@ -385,5 +385,19 @@
|
|
|
385
385
|
"ai-assistant.usage.total": "Jumlah",
|
|
386
386
|
"ai-assistant.text.send-to-people-notice": "Ini dihantar kepada peserta perbualan ini. AI tidak menjawabnya.",
|
|
387
387
|
"task.ai-assistant.use.name": "Menggunakan pembantu AI",
|
|
388
|
-
"task.ai-assistant.use.description": "Berbual dengan pembantu. Ia hanya membaca melalui keistimewaan pemanggil."
|
|
388
|
+
"task.ai-assistant.use.description": "Berbual dengan pembantu. Ia hanya membaca melalui keistimewaan pemanggil.",
|
|
389
|
+
"ai-assistant.run.assistant-run-as-unknown": "akaun yang sepatutnya digunakan untuk larian ini tidak wujud.",
|
|
390
|
+
"ai-assistant.run.assistant-run-as-inactive": "akaun pemilik tidak aktif, jadi larian tidak dimulakan.",
|
|
391
|
+
"ai-assistant.run.assistant-run-as-not-member": "pemilik tidak lagi boleh memasuki domain ini, jadi larian tidak dimulakan.",
|
|
392
|
+
"ai-assistant.run.assistant-run-as-not-permitted": "pemilik tidak lagi dibenarkan menggunakan pembantu AI, jadi larian tidak dimulakan.",
|
|
393
|
+
"ai-assistant.run.assistant-mode-not-unattended": "mod pembantu {kind} tidak berjalan tanpa orang.",
|
|
394
|
+
"ai-assistant.run.unknown-assistant-mode": "pelayan ini tidak mempunyai mod pembantu {kind}.",
|
|
395
|
+
"ai-assistant.run.assistant-run-budget-exceeded": "larian mencapai had {limit} ({used} daripada {max}) dan berhenti.",
|
|
396
|
+
"ai-assistant.run.assistant-run-step-limit": "larian tidak selesai dalam {steps} langkah.",
|
|
397
|
+
"ai-assistant.run.assistant-run-timeout": "larian tidak selesai dalam {timeoutMs} ms.",
|
|
398
|
+
"ai-assistant.run.assistant-run-empty-draft": "larian tamat tanpa menulis draf.",
|
|
399
|
+
"ai-assistant.run.assistant-run-model-error": "model AI gagal semasa larian.",
|
|
400
|
+
"ai-assistant.run.assistant-run-abandoned": "larian terhenti di tengah jalan dan dimulakan semula.",
|
|
401
|
+
"ai-assistant.run.ai-not-configured": "AI belum disediakan untuk domain ini.",
|
|
402
|
+
"ai-assistant.run.ai-agent-not-available": "ejen AI yang dipilih untuk larian ini tidak tersedia di sini."
|
|
389
403
|
}
|
package/translations/zh.json
CHANGED
|
@@ -385,5 +385,19 @@
|
|
|
385
385
|
"ai-assistant.usage.total": "合计",
|
|
386
386
|
"ai-assistant.text.send-to-people-notice": "此消息发送给本会话的参与者,AI 不会回复。",
|
|
387
387
|
"task.ai-assistant.use.name": "使用 AI 助手",
|
|
388
|
-
"task.ai-assistant.use.description": "与 AI 助手对话。助手只以调用者的权限读取。"
|
|
388
|
+
"task.ai-assistant.use.description": "与 AI 助手对话。助手只以调用者的权限读取。",
|
|
389
|
+
"ai-assistant.run.assistant-run-as-unknown": "要代为执行的用户账户不存在。",
|
|
390
|
+
"ai-assistant.run.assistant-run-as-inactive": "所有者账户未处于启用状态,因此未执行。",
|
|
391
|
+
"ai-assistant.run.assistant-run-as-not-member": "所有者已无法进入此站点,因此未执行。",
|
|
392
|
+
"ai-assistant.run.assistant-run-as-not-permitted": "所有者已无权使用 AI 助手,因此未执行。",
|
|
393
|
+
"ai-assistant.run.assistant-mode-not-unattended": "助手模式 {kind} 不能在无人时运行。",
|
|
394
|
+
"ai-assistant.run.unknown-assistant-mode": "此服务器没有助手模式 {kind}。",
|
|
395
|
+
"ai-assistant.run.assistant-run-budget-exceeded": "运行达到 {limit} 上限({used}/{max})后停止。",
|
|
396
|
+
"ai-assistant.run.assistant-run-step-limit": "运行未在 {steps} 步内完成。",
|
|
397
|
+
"ai-assistant.run.assistant-run-timeout": "运行未在 {timeoutMs} 毫秒内完成。",
|
|
398
|
+
"ai-assistant.run.assistant-run-empty-draft": "运行结束但未写出草稿。",
|
|
399
|
+
"ai-assistant.run.assistant-run-model-error": "运行期间 AI 模型调用失败。",
|
|
400
|
+
"ai-assistant.run.assistant-run-abandoned": "运行中途停止,已重新开始。",
|
|
401
|
+
"ai-assistant.run.ai-not-configured": "此站点尚未设置 AI。",
|
|
402
|
+
"ai-assistant.run.ai-agent-not-available": "为此运行选择的 AI 代理在此不可用。"
|
|
389
403
|
}
|